diff --git a/.docker-hub-test b/.docker-hub-test
deleted file mode 100644
index 6c2dff5ab..000000000
--- a/.docker-hub-test
+++ /dev/null
@@ -1 +0,0 @@
-Docker Hub test 2
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 000000000..065d2a7e6
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,12 @@
+/src-ui/.vscode
+/src-ui/node_modules
+/src-ui/dist
+.git
+/export
+/consume
+/media
+/data
+/docs
+.pytest_cache
+/dist
+/scripts
diff --git a/.env b/.env
new file mode 100644
index 000000000..511a1386d
--- /dev/null
+++ b/.env
@@ -0,0 +1 @@
+COMPOSE_PROJECT_NAME=paperless
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index 8863fa346..000000000
--- a/.gitattributes
+++ /dev/null
@@ -1 +0,0 @@
-THANKS.md merge=union
diff --git a/.gitignore b/.gitignore
index 957d47743..d63794fb3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -57,16 +57,6 @@ docs/_build/
# PyBuilder
target/
-# Stored PDFs
-media/documents/*.gpg
-media/documents/thumbnails/*
-media/documents/originals/*
-media/overrides.css
-media/overrides.js
-
-# Sqlite database
-db.sqlite3
-db.sqlite3-journal
# PyCharm
.idea
@@ -74,12 +64,24 @@ db.sqlite3-journal
# Other stuff that doesn't belong
.virtualenv
virtualenv
-docker-compose.yml
+/venv
docker-compose.env
+docker-compose.yml
# Used for development
scripts/import-for-development
scripts/nuke
# Static files collected by the collectstatic command
-./static/
+/static/
+
+# Stored PDFs
+/media/
+/data/
+/paperless.conf
+/consume/
+/export/
+/src-ui/.vscode
+
+# this is where the compiled frontend is moved to.
+/src/documents/static/frontend/
diff --git a/.travis.yml b/.travis.yml
index fa1785d8d..b745d6bd7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,56 +1,51 @@
language: python
+dist: focal
+os: linux
+
+jobs:
+ include:
+ - name: "Paperless on Python 3.6"
+ python: "3.6"
+
+ - name: "Paperless on Python 3.7"
+ python: "3.7"
+
+ - name: "Paperless on Python 3.8"
+ python: "3.8"
+
+ - name: "Documentation"
+ script:
+ - cd docs/
+ - make html
+ after_success: true
+
+ - name: "Front end"
+ language: node_js
+ node_js:
+ - 15
+ before_install: true
+ install:
+ - cd src-ui/
+ - npm install -g @angular/cli
+ - npm install
+ script:
+ - ng build --prod
+ after_success: true
+
+
before_install:
-- sudo apt-get update -qq
-- sudo apt-get install -qq libpoppler-cpp-dev unpaper tesseract-ocr
-
-sudo: false
-
-matrix:
- include:
- - python: "3.5"
- - python: "3.6"
- - python: "3.7-dev"
- - env:
- - BUILD_DOCKER=1
- # Variable to add to publish the Docker image:
- # * DOCKER_USERNAME
- # * DOCKER_PASSWORD, to be encrypted, use `travis encrypt DOCKER_PASSWORD=`
- services:
- - docker
- before_install:
- - true
- install:
- - true
- script:
- - docker build --tag=the-paperless-project/paperless .
- after_success:
- - true
+ - sudo apt-get update -qq
+ - sudo apt-get install -qq libpoppler-cpp-dev unpaper tesseract-ocr imagemagick ghostscript optipng
install:
- - pip install --upgrade pip pipenv sphinx
- - pipenv lock -r > requirements.txt
- - pip install -r requirements.txt
+ - pip install --upgrade pipenv
+ - pipenv install --system --dev
script:
- - cd src/
- - pytest --cov
- - pycodestyle
- - sphinx-build -b html ../docs ../docs/_build -W
+ - cd src/
+ - pipenv run pytest --cov
+ - pipenv run pycodestyle
after_success:
- - coveralls
-
-deploy:
- - provider: script
- skip_cleanup: true
- script: ci/deploy-docker
- on:
- tags: true
- condition: '"${BUILD_DOCKER}" = 1'
- - provider: script
- skip_cleanup: true
- script: ci/deploy-docker
- on:
- branch: master
- condition: '"${BUILD_DOCKER}" = 1'
+ - pipenv run coveralls
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
deleted file mode 100644
index d57b2e7e5..000000000
--- a/CODE_OF_CONDUCT.md
+++ /dev/null
@@ -1,46 +0,0 @@
-# Contributor Covenant Code of Conduct
-
-## Our Pledge
-
-In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
-
-## Our Standards
-
-Examples of behavior that contributes to creating a positive environment include:
-
-* Using welcoming and inclusive language
-* Being respectful of differing viewpoints and experiences
-* Gracefully accepting constructive criticism
-* Focusing on what is best for the community
-* Showing empathy towards other community members
-
-Examples of unacceptable behavior by participants include:
-
-* Unwelcome sexual attention or advances
-* Trolling, insulting/derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or electronic address, without explicit permission
-* Other conduct which could reasonably be considered inappropriate in a professional setting
-
-## Our Responsibilities
-
-Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
-
-Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
-
-## Scope
-
-This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
-
-## Enforcement
-
-Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at code@danielquinn.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
-
-Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
-
-## Attribution
-
-This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4 to remove puritanical language. The original is available at [http://contributor-covenant.org/version/1/4][version]
-
-[homepage]: http://contributor-covenant.org
-[version]: http://contributor-covenant.org/version/1/4/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..a8fb1f8e9
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,30 @@
+# Contributing
+
+There's still lots of things to be done, just have a look at that issue log. If you feel like conctributing to the project, please do! Bug fixes and improvements to the front end (I just can't seem to get some of these CSS things right) are always welcome.
+
+If you want to implement something big: Please start a discussion about that in the issues! Maybe I've already had something similar in mind and we can make it happen together. However, keep in mind that the general roadmap is to make the existing features stable and get them tested. See the roadmap in the readme.
+
+* When making additions to the project, consider if the majority of users will benefit from your change. If not, you're probably better of forking the project.
+* Also consider if your change will get in the way of other users. A good change is a change that enhances the experience of some users who want that change and does not affect users who do not care about the change.
+
+## Python
+
+Use python 3.6 for development. Paperless supports python 3.6, 3.7 and 3.8.
+
+## Branches
+
+master always reflects the latest release.
+
+dev contains all changes that will be part of the next release. Use this branch to start making your changes.
+
+feature-X branches is for experimental stuff that will eventually be merged into dev, and then released as part of the next release.
+
+## Testing:
+
+I'm trying to get most of paperless tested, so please do the same for your code! I know its a hassle, but it makes sure that your code works now and will allow us to detect regressions easily.
+
+To test your code, execute `pytest` in the src/ directory. Executing that in the project root is no good. This also generates a html coverage report, which you can use to see if you missed anything important during testing.
+
+## More info:
+
+... is available in the documentation. https://paperless-ng.readthedocs.io/en/latest/extending.html
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 2b95288e6..000000000
--- a/Dockerfile
+++ /dev/null
@@ -1,74 +0,0 @@
-FROM alpine:3.11
-
-LABEL maintainer="The Paperless Project https://github.com/the-paperless-project/paperless" \
- contributors="Guy Addadi , Pit Kleyersburg , \
- Sven Fischer "
-
-# Copy Pipfiles file, init script and gunicorn.conf
-COPY Pipfile* /usr/src/paperless/
-COPY scripts/docker-entrypoint.sh /sbin/docker-entrypoint.sh
-COPY scripts/gunicorn.conf /usr/src/paperless/
-
-# Set export and consumption directories
-ENV PAPERLESS_EXPORT_DIR=/export \
- PAPERLESS_CONSUMPTION_DIR=/consume
-
-RUN apk add --no-cache \
- bash \
- curl \
- ghostscript \
- gnupg \
- imagemagick \
- libmagic \
- libpq \
- optipng \
- poppler \
- python3 \
- shadow \
- sudo \
- tesseract-ocr \
- tzdata \
- unpaper && \
- apk add --no-cache --virtual .build-dependencies \
- g++ \
- gcc \
- jpeg-dev \
- musl-dev \
- poppler-dev \
- postgresql-dev \
- python3-dev \
- zlib-dev && \
-# Install python dependencies
- python3 -m ensurepip && \
- rm -r /usr/lib/python*/ensurepip && \
- cd /usr/src/paperless && \
- pip3 install --upgrade pip pipenv && \
- pipenv install --system --deploy && \
-# Remove build dependencies
- apk del .build-dependencies && \
-# Create the consumption directory
- mkdir -p $PAPERLESS_CONSUMPTION_DIR && \
-# Create user
- addgroup -g 1000 paperless && \
- adduser -D -u 1000 -G paperless -h /usr/src/paperless paperless && \
- chown -Rh paperless:paperless /usr/src/paperless && \
- mkdir -p $PAPERLESS_EXPORT_DIR && \
-# Avoid setrlimit warnings
-# See: https://gitlab.alpinelinux.org/alpine/aports/issues/11122
- echo 'Set disable_coredump false' >> /etc/sudo.conf && \
-# Setup entrypoint
- chmod 755 /sbin/docker-entrypoint.sh
-
-WORKDIR /usr/src/paperless/src
-# Mount volumes and set Entrypoint
-VOLUME ["/usr/src/paperless/data", "/usr/src/paperless/media", "/consume", "/export"]
-ENTRYPOINT ["/sbin/docker-entrypoint.sh"]
-CMD ["--help"]
-
-# Copy application
-COPY src/ /usr/src/paperless/src/
-COPY data/ /usr/src/paperless/data/
-COPY media/ /usr/src/paperless/media/
-
-# Collect static files
-RUN sudo -HEu paperless /usr/src/paperless/src/manage.py collectstatic --clear --no-input
diff --git a/Pipfile b/Pipfile
index fa7282ce3..48759307c 100644
--- a/Pipfile
+++ b/Pipfile
@@ -3,41 +3,56 @@ url = "https://pypi.python.org/simple"
verify_ssl = true
name = "pypi"
+[[source]]
+url = "https://www.piwheels.org/simple"
+verify_ssl = true
+name = "piwheels"
+
+[requires]
+python_version = "3.6"
+
[packages]
-django = "<2.1,>=2.0"
-pillow = "*"
-coveralls = "*"
-dateparser = "*"
+dateparser = "~=0.7.6"
+django = "~=3.1.3"
django-cors-headers = "*"
-django-crispy-forms = "*"
django-extensions = "*"
-django-filter = "*"
-djangorestframework = "*"
-factory-boy = "*"
-filemagic = "*"
-fuzzywuzzy = {extras = ["speedup"],version = "==0.15.0"}
+django-filter = "~=2.4.0"
+django-q = "~=1.3.4"
+djangorestframework = "~=3.12.2"
+filelock = "*"
+fuzzywuzzy = "*"
gunicorn = "*"
-inotify-simple = "*"
+imap-tools = "*"
langdetect = "*"
pdftotext = "*"
-pyocr = "*"
-python-dateutil = "*"
-python-dotenv = "*"
+pathvalidate = "*"
+pillow = "*"
+pikepdf = "*"
python-gnupg = "*"
-pytz = "*"
-sphinx = "*"
-tox = "*"
+python-dotenv = "*"
+python-dateutil = "*"
+python-Levenshtein = "*"
+python-magic = "*"
+psycopg2-binary = "*"
+redis = "*"
+scikit-learn="~=0.23.2"
+whitenoise = "~=5.2.0"
+watchdog = "*"
+whoosh="~=2.7.4"
+inotifyrecursive = "~=0.3.4"
+ocrmypdf = "*"
+tqdm = "*"
+
+[dev-packages]
+coveralls = "*"
+factory-boy = "*"
pycodestyle = "*"
pytest = "*"
pytest-cov = "*"
pytest-django = "*"
-pytest-sugar = "*"
pytest-env = "*"
+pytest-sugar = "*"
pytest-xdist = "*"
-psycopg2 = "*"
-djangoql = "*"
-whitenoise = "*"
-brotli = "*"
-
-[dev-packages]
-ipython = "*"
+sphinx = "~=3.3"
+sphinx_rtd_theme = "*"
+tox = "*"
diff --git a/Pipfile.lock b/Pipfile.lock
index 186cb2c83..1cfccb8ff 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,19 +1,831 @@
{
"_meta": {
"hash": {
- "sha256": "229095b1d386419c9716ea1fbb7b42885d6ff686ca7266d317fbac78456754ac"
+ "sha256": "3d576f289958226a7583e4c471c7f8c11bff6933bf093185f623cfb381a92412"
},
"pipfile-spec": 6,
- "requires": {},
+ "requires": {
+ "python_version": "3.6"
+ },
"sources": [
{
"name": "pypi",
"url": "https://pypi.python.org/simple",
"verify_ssl": true
+ },
+ {
+ "name": "piwheels",
+ "url": "https://www.piwheels.org/simple",
+ "verify_ssl": true
}
]
},
"default": {
+ "arrow": {
+ "hashes": [
+ "sha256:e098abbd9af3665aea81bdd6c869e93af4feb078e98468dd351c383af187aac5",
+ "sha256:ff08d10cda1d36c68657d6ad20d74fbea493d980f8b2d45344e00d6ed2bf6ed4"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==0.17.0"
+ },
+ "asgiref": {
+ "hashes": [
+ "sha256:5ee950735509d04eb673bd7f7120f8fa1c9e2df495394992c73234d526907e17",
+ "sha256:7162a3cb30ab0609f1a4c95938fd73e8604f63bdba516a7f7d64b83ff09478f0"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==3.3.1"
+ },
+ "blessed": {
+ "hashes": [
+ "sha256:0a74a8d3f0366db600d061273df77d44f0db07daade7bb7a4d49c8bc22ed9f74",
+ "sha256:580429e7e0c6f6a42ea81b0ae5a4993b6205c6ccbb635d034b4277af8175753e"
+ ],
+ "version": "==1.17.12"
+ },
+ "cffi": {
+ "hashes": [
+ "sha256:00a1ba5e2e95684448de9b89888ccd02c98d512064b4cb987d48f4b40aa0421e",
+ "sha256:00e28066507bfc3fe865a31f325c8391a1ac2916219340f87dfad602c3e48e5d",
+ "sha256:045d792900a75e8b1e1b0ab6787dd733a8190ffcf80e8c8ceb2fb10a29ff238a",
+ "sha256:0638c3ae1a0edfb77c6765d487fee624d2b1ee1bdfeffc1f0b58c64d149e7eec",
+ "sha256:105abaf8a6075dc96c1fe5ae7aae073f4696f2905fde6aeada4c9d2926752362",
+ "sha256:155136b51fd733fa94e1c2ea5211dcd4c8879869008fc811648f16541bf99668",
+ "sha256:1a465cbe98a7fd391d47dce4b8f7e5b921e6cd805ef421d04f5f66ba8f06086c",
+ "sha256:1d2c4994f515e5b485fd6d3a73d05526aa0fcf248eb135996b088d25dfa1865b",
+ "sha256:23f318bf74b170c6e9adb390e8bd282457f6de46c19d03b52f3fd042b5e19654",
+ "sha256:2c24d61263f511551f740d1a065eb0212db1dbbbbd241db758f5244281590c06",
+ "sha256:51a8b381b16ddd370178a65360ebe15fbc1c71cf6f584613a7ea08bfad946698",
+ "sha256:594234691ac0e9b770aee9fcdb8fa02c22e43e5c619456efd0d6c2bf276f3eb2",
+ "sha256:5cf4be6c304ad0b6602f5c4e90e2f59b47653ac1ed9c662ed379fe48a8f26b0c",
+ "sha256:64081b3f8f6f3c3de6191ec89d7dc6c86a8a43911f7ecb422c60e90c70be41c7",
+ "sha256:6bc25fc545a6b3d57b5f8618e59fc13d3a3a68431e8ca5fd4c13241cd70d0009",
+ "sha256:798caa2a2384b1cbe8a2a139d80734c9db54f9cc155c99d7cc92441a23871c03",
+ "sha256:7c6b1dece89874d9541fc974917b631406233ea0440d0bdfbb8e03bf39a49b3b",
+ "sha256:840793c68105fe031f34d6a086eaea153a0cd5c491cde82a74b420edd0a2b909",
+ "sha256:8d6603078baf4e11edc4168a514c5ce5b3ba6e3e9c374298cb88437957960a53",
+ "sha256:9cc46bc107224ff5b6d04369e7c595acb700c3613ad7bcf2e2012f62ece80c35",
+ "sha256:9f7a31251289b2ab6d4012f6e83e58bc3b96bd151f5b5262467f4bb6b34a7c26",
+ "sha256:9ffb888f19d54a4d4dfd4b3f29bc2c16aa4972f1c2ab9c4ab09b8ab8685b9c2b",
+ "sha256:a5ed8c05548b54b998b9498753fb9cadbfd92ee88e884641377d8a8b291bcc01",
+ "sha256:a7711edca4dcef1a75257b50a2fbfe92a65187c47dab5a0f1b9b332c5919a3fb",
+ "sha256:af5c59122a011049aad5dd87424b8e65a80e4a6477419c0c1015f73fb5ea0293",
+ "sha256:b18e0a9ef57d2b41f5c68beefa32317d286c3d6ac0484efd10d6e07491bb95dd",
+ "sha256:b4e248d1087abf9f4c10f3c398896c87ce82a9856494a7155823eb45a892395d",
+ "sha256:ba4e9e0ae13fc41c6b23299545e5ef73055213e466bd107953e4a013a5ddd7e3",
+ "sha256:be8661bcee1bc2fc4b033a6ab65bd1f87ce5008492601695d0b9a4e820c3bde5",
+ "sha256:c6332685306b6417a91b1ff9fae889b3ba65c2292d64bd9245c093b1b284809d",
+ "sha256:d5ff0621c88ce83a28a10d2ce719b2ee85635e85c515f12bac99a95306da4b2e",
+ "sha256:d9efd8b7a3ef378dd61a1e77367f1924375befc2eba06168b6ebfa903a5e59ca",
+ "sha256:df5169c4396adc04f9b0a05f13c074df878b6052430e03f50e68adf3a57aa28d",
+ "sha256:ebb253464a5d0482b191274f1c8bf00e33f7e0b9c66405fbffc61ed2c839c775",
+ "sha256:ec80dc47f54e6e9a78181ce05feb71a0353854cc26999db963695f950b5fb375",
+ "sha256:f032b34669220030f905152045dfa27741ce1a6db3324a5bc0b96b6c7420c87b",
+ "sha256:f60567825f791c6f8a592f3c6e3bd93dd2934e3f9dac189308426bd76b00ef3b",
+ "sha256:f803eaa94c2fcda012c047e62bc7a51b0bdabda1cad7a92a522694ea2d76e49f"
+ ],
+ "version": "==1.14.4"
+ },
+ "chardet": {
+ "hashes": [
+ "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
+ "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
+ ],
+ "markers": "python_version >= '3.1'",
+ "version": "==3.0.4"
+ },
+ "coloredlogs": {
+ "hashes": [
+ "sha256:346f58aad6afd48444c2468618623638dadab76e4e70d5e10822676f2d32226a",
+ "sha256:a1fab193d2053aa6c0a97608c4342d031f1f93a3d1218432c59322441d31a505",
+ "sha256:b0c2124367d4f72bd739f48e1f61491b4baf145d6bda33b606b4a53cb3f96a97"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==14.0"
+ },
+ "cryptography": {
+ "hashes": [
+ "sha256:07ca431b788249af92764e3be9a488aa1d39a0bc3be313d826bbec690417e538",
+ "sha256:13b88a0bd044b4eae1ef40e265d006e34dbcde0c2f1e15eb9896501b2d8f6c6f",
+ "sha256:257dab4f368fae15f378ea9a4d2799bf3696668062de0e9fa0ebb7a738a6917d",
+ "sha256:32434673d8505b42c0de4de86da8c1620651abd24afe91ae0335597683ed1b77",
+ "sha256:3cd75a683b15576cfc822c7c5742b3276e50b21a06672dc3a800a2d5da4ecd1b",
+ "sha256:4e7268a0ca14536fecfdf2b00297d4e407da904718658c1ff1961c713f90fd33",
+ "sha256:545a8550782dda68f8cdc75a6e3bf252017aa8f75f19f5a9ca940772fc0cb56e",
+ "sha256:55d0b896631412b6f0c7de56e12eb3e261ac347fbaa5d5e705291a9016e5f8cb",
+ "sha256:5849d59358547bf789ee7e0d7a9036b2d29e9a4ddf1ce5e06bb45634f995c53e",
+ "sha256:59f7d4cfea9ef12eb9b14b83d79b432162a0a24a91ddc15c2c9bf76a68d96f2b",
+ "sha256:6dc59630ecce8c1f558277ceb212c751d6730bd12c80ea96b4ac65637c4f55e7",
+ "sha256:7117319b44ed1842c617d0a452383a5a052ec6aa726dfbaffa8b94c910444297",
+ "sha256:75e8e6684cf0034f6bf2a97095cb95f81537b12b36a8fedf06e73050bb171c2d",
+ "sha256:7b8d9d8d3a9bd240f453342981f765346c87ade811519f98664519696f8e6ab7",
+ "sha256:a035a10686532b0587d58a606004aa20ad895c60c4d029afa245802347fab57b",
+ "sha256:a4e27ed0b2504195f855b52052eadcc9795c59909c9d84314c5408687f933fc7",
+ "sha256:a733671100cd26d816eed39507e585c156e4498293a907029969234e5e634bc4",
+ "sha256:a75f306a16d9f9afebfbedc41c8c2351d8e61e818ba6b4c40815e2b5740bb6b8",
+ "sha256:bd717aa029217b8ef94a7d21632a3bb5a4e7218a4513d2521c2a2fd63011e98b",
+ "sha256:d25cecbac20713a7c3bc544372d42d8eafa89799f492a43b79e1dfd650484851",
+ "sha256:d26a2557d8f9122f9bf445fc7034242f4375bd4e95ecda007667540270965b13",
+ "sha256:d3545829ab42a66b84a9aaabf216a4dce7f16dbc76eb69be5c302ed6b8f4a29b",
+ "sha256:d3d5e10be0cf2a12214ddee45c6bd203dab435e3d83b4560c03066eda600bfe3",
+ "sha256:efe15aca4f64f3a7ea0c09c87826490e50ed166ce67368a68f315ea0807a20df"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==3.2.1"
+ },
+ "dateparser": {
+ "hashes": [
+ "sha256:7552c994f893b5cb8fcf103b4cd2ff7f57aab9bfd2619fdf0cf571c0740fd90b",
+ "sha256:e875efd8c57c85c2d02b238239878db59ff1971f5a823457fcc69e493bf6ebfa"
+ ],
+ "index": "pypi",
+ "version": "==0.7.6"
+ },
+ "django": {
+ "hashes": [
+ "sha256:5c866205f15e7a7123f1eec6ab939d22d5bde1416635cab259684af66d8e48a2",
+ "sha256:edb10b5c45e7e9c0fb1dc00b76ec7449aca258a39ffd613dbd078c51d19c9f03"
+ ],
+ "index": "pypi",
+ "version": "==3.1.4"
+ },
+ "django-cors-headers": {
+ "hashes": [
+ "sha256:9322255c296d5f75089571f29e520c83ff9693df17aa3cf9f6a4bea7c6740169",
+ "sha256:db82b2840f667d47872ae3e4a4e0a0d72fbecb42779b8aa233fa8bb965f7836a"
+ ],
+ "index": "pypi",
+ "version": "==3.5.0"
+ },
+ "django-extensions": {
+ "hashes": [
+ "sha256:7cd002495ff0a0e5eb6cdd6be759600905b4e4079232ea27618fc46bdd853651",
+ "sha256:c7f88625a53f631745d4f2bef9ec4dcb999ed59476393bdbbe99db8596778846"
+ ],
+ "index": "pypi",
+ "version": "==3.1.0"
+ },
+ "django-filter": {
+ "hashes": [
+ "sha256:84e9d5bb93f237e451db814ed422a3a625751cbc9968b484ecc74964a8696b06",
+ "sha256:e00d32cebdb3d54273c48f4f878f898dced8d5dfaad009438fe61ebdf535ace1"
+ ],
+ "index": "pypi",
+ "version": "==2.4.0"
+ },
+ "django-picklefield": {
+ "hashes": [
+ "sha256:15ccba592ca953b9edf9532e64640329cd47b136b7f8f10f2939caa5f9ce4287",
+ "sha256:3c702a54fde2d322fe5b2f39b8f78d9f655b8f77944ab26f703be6c0ed335a35"
+ ],
+ "markers": "python_version >= '3'",
+ "version": "==3.0.1"
+ },
+ "django-q": {
+ "hashes": [
+ "sha256:523d54dcf1b66152c1b658f914f00ed3b518a3432a9decd4898738ca8dbbe10f",
+ "sha256:7e5c5c021a15cff6807044a3aa48f5757789ccfef839d71c575f5512931a3e33"
+ ],
+ "index": "pypi",
+ "version": "==1.3.4"
+ },
+ "djangorestframework": {
+ "hashes": [
+ "sha256:0209bafcb7b5010fdfec784034f059d512256424de2a0f084cb82b096d6dd6a7"
+ ],
+ "index": "pypi",
+ "version": "==3.12.2"
+ },
+ "filelock": {
+ "hashes": [
+ "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59",
+ "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"
+ ],
+ "index": "pypi",
+ "version": "==3.0.12"
+ },
+ "fuzzywuzzy": {
+ "hashes": [
+ "sha256:45016e92264780e58972dca1b3d939ac864b78437422beecebb3095f8efd00e8",
+ "sha256:928244b28db720d1e0ee7587acf660ea49d7e4c632569cad4f1cd7e68a5f0993"
+ ],
+ "index": "pypi",
+ "version": "==0.18.0"
+ },
+ "gunicorn": {
+ "hashes": [
+ "sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626",
+ "sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c"
+ ],
+ "index": "pypi",
+ "version": "==20.0.4"
+ },
+ "humanfriendly": {
+ "hashes": [
+ "sha256:175ffa628aa76da2c17369a5da5856084562cc66dfe7f82ae93ca3ef175277a6",
+ "sha256:3c9ab8d28e88e6cc998e41963357736dafd555ee5bb666b50e42f6ce28dd3e3d"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==9.0"
+ },
+ "imap-tools": {
+ "hashes": [
+ "sha256:72bf46dc135b039a5d5b59f4e079242ac15eac02a30038e8cb2dec7b153cab65",
+ "sha256:75dc1c72dd76d9e577df26a1e0ec3a809b5eebce77678851458dcd2eae127ac9"
+ ],
+ "index": "pypi",
+ "version": "==0.33.0"
+ },
+ "img2pdf": {
+ "hashes": [
+ "sha256:57905015579b1026acf1605aa95859cd79b051fa1c35485573d165526fc9dbb5",
+ "sha256:eaee690ab8403dd1a9cb4db10afee41dd3e6c7ed63bdace02a0121f9feadb0c9"
+ ],
+ "version": "==0.4.0"
+ },
+ "importlib-metadata": {
+ "hashes": [
+ "sha256:6112e21359ef8f344e7178aa5b72dc6e62b38b0d008e6d3cb212c5b84df72013",
+ "sha256:b0c2d3b226157ae4517d9625decf63591461c66b3a808c2666d538946519d170"
+ ],
+ "markers": "python_version < '3.8'",
+ "version": "==3.1.1"
+ },
+ "inotify-simple": {
+ "hashes": [
+ "sha256:8440ffe49c4ae81a8df57c1ae1eb4b6bfa7acb830099bfb3e305b383005cc128",
+ "sha256:854f9ac752cc1fcff6ca34e9d3d875c9a94c9b7d6eb377f63be2d481a566c6ee"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==1.3.5"
+ },
+ "inotifyrecursive": {
+ "hashes": [
+ "sha256:7e5f4a2e1dc2bef0efa3b5f6b339c41fb4599055a2b54909d020e9e932cc8d2f",
+ "sha256:a2c450b317693e4538416f90eb1d7858506dafe6b8b885037bd2dd9ae2dafa1e"
+ ],
+ "index": "pypi",
+ "version": "==0.3.5"
+ },
+ "joblib": {
+ "hashes": [
+ "sha256:698c311779f347cf6b7e6b8a39bb682277b8ee4aba8cf9507bc0cf4cd4737b72",
+ "sha256:9e284edd6be6b71883a63c9b7f124738a3c16195513ad940eae7e3438de885d5"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==0.17.0"
+ },
+ "langdetect": {
+ "hashes": [
+ "sha256:363795ea005f1243c958e953245dac5d814fabdc025c9afa91588c5fa6b2fa83",
+ "sha256:ae53a024643df713274c297c0795dbfb5a16b329902f8e543e7b2d7d45f699e4",
+ "sha256:f37495e63607865e47deed08d78f7f8e58172658216ff954b2f14671bcd87740"
+ ],
+ "index": "pypi",
+ "version": "==1.0.8"
+ },
+ "lxml": {
+ "hashes": [
+ "sha256:0448576c148c129594d890265b1a83b9cd76fd1f0a6a04620753d9a6bcfd0a4d",
+ "sha256:127f76864468d6630e1b453d3ffbbd04b024c674f55cf0a30dc2595137892d37",
+ "sha256:1471cee35eba321827d7d53d104e7b8c593ea3ad376aa2df89533ce8e1b24a01",
+ "sha256:2363c35637d2d9d6f26f60a208819e7eafc4305ce39dc1d5005eccc4593331c2",
+ "sha256:2e5cc908fe43fe1aa299e58046ad66981131a66aea3129aac7770c37f590a644",
+ "sha256:2e6fd1b8acd005bd71e6c94f30c055594bbd0aa02ef51a22bbfa961ab63b2d75",
+ "sha256:366cb750140f221523fa062d641393092813b81e15d0e25d9f7c6025f910ee80",
+ "sha256:42ebca24ba2a21065fb546f3e6bd0c58c3fe9ac298f3a320147029a4850f51a2",
+ "sha256:4e751e77006da34643ab782e4a5cc21ea7b755551db202bc4d3a423b307db780",
+ "sha256:4fb85c447e288df535b17ebdebf0ec1cf3a3f1a8eba7e79169f4f37af43c6b98",
+ "sha256:50c348995b47b5a4e330362cf39fc503b4a43b14a91c34c83b955e1805c8e308",
+ "sha256:535332fe9d00c3cd455bd3dd7d4bacab86e2d564bdf7606079160fa6251caacf",
+ "sha256:535f067002b0fd1a4e5296a8f1bf88193080ff992a195e66964ef2a6cfec5388",
+ "sha256:5be4a2e212bb6aa045e37f7d48e3e1e4b6fd259882ed5a00786f82e8c37ce77d",
+ "sha256:60a20bfc3bd234d54d49c388950195d23a5583d4108e1a1d47c9eef8d8c042b3",
+ "sha256:648914abafe67f11be7d93c1a546068f8eff3c5fa938e1f94509e4a5d682b2d8",
+ "sha256:681d75e1a38a69f1e64ab82fe4b1ed3fd758717bed735fb9aeaa124143f051af",
+ "sha256:68a5d77e440df94011214b7db907ec8f19e439507a70c958f750c18d88f995d2",
+ "sha256:69a63f83e88138ab7642d8f61418cf3180a4d8cd13995df87725cb8b893e950e",
+ "sha256:6e4183800f16f3679076dfa8abf2db3083919d7e30764a069fb66b2b9eff9939",
+ "sha256:6fd8d5903c2e53f49e99359b063df27fdf7acb89a52b6a12494208bf61345a03",
+ "sha256:791394449e98243839fa822a637177dd42a95f4883ad3dec2a0ce6ac99fb0a9d",
+ "sha256:7a7669ff50f41225ca5d6ee0a1ec8413f3a0d8aa2b109f86d540887b7ec0d72a",
+ "sha256:7e9eac1e526386df7c70ef253b792a0a12dd86d833b1d329e038c7a235dfceb5",
+ "sha256:7ee8af0b9f7de635c61cdd5b8534b76c52cd03536f29f51151b377f76e214a1a",
+ "sha256:8246f30ca34dc712ab07e51dc34fea883c00b7ccb0e614651e49da2c49a30711",
+ "sha256:8c88b599e226994ad4db29d93bc149aa1aff3dc3a4355dd5757569ba78632bdf",
+ "sha256:91d6dace31b07ab47eeadd3f4384ded2f77b94b30446410cb2c3e660e047f7a7",
+ "sha256:923963e989ffbceaa210ac37afc9b906acebe945d2723e9679b643513837b089",
+ "sha256:94d55bd03d8671686e3f012577d9caa5421a07286dd351dfef64791cf7c6c505",
+ "sha256:97db258793d193c7b62d4e2586c6ed98d51086e93f9a3af2b2034af01450a74b",
+ "sha256:a9d6bc8642e2c67db33f1247a77c53476f3a166e09067c0474facb045756087f",
+ "sha256:cd11c7e8d21af997ee8079037fff88f16fda188a9776eb4b81c7e4c9c0a7d7fc",
+ "sha256:d8d3d4713f0c28bdc6c806a278d998546e8efc3498949e3ace6e117462ac0a5e",
+ "sha256:e0bfe9bb028974a481410432dbe1b182e8191d5d40382e5b8ff39cdd2e5c5931",
+ "sha256:e1dbb88a937126ab14d219a000728224702e0ec0fc7ceb7131c53606b7a76772",
+ "sha256:f4822c0660c3754f1a41a655e37cb4dbbc9be3d35b125a37fab6f82d47674ebc",
+ "sha256:f83d281bb2a6217cd806f4cf0ddded436790e66f393e124dfe9731f6b3fb9afe",
+ "sha256:fc37870d6716b137e80d19241d0e2cff7a7643b925dfa49b4c8ebd1295eb506e"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==4.6.2"
+ },
+ "numpy": {
+ "hashes": [
+ "sha256:08308c38e44cc926bdfce99498b21eec1f848d24c302519e64203a8da99a97db",
+ "sha256:09c12096d843b90eafd01ea1b3307e78ddd47a55855ad402b157b6c4862197ce",
+ "sha256:13d166f77d6dc02c0a73c1101dd87fdf01339febec1030bd810dcd53fff3b0f1",
+ "sha256:141ec3a3300ab89c7f2b0775289954d193cc8edb621ea05f99db9cb181530512",
+ "sha256:16c1b388cc31a9baa06d91a19366fb99ddbe1c7b205293ed072211ee5bac1ed2",
+ "sha256:18bed2bcb39e3f758296584337966e68d2d5ba6aab7e038688ad53c8f889f757",
+ "sha256:1aeef46a13e51931c0b1cf8ae1168b4a55ecd282e6688fdb0a948cc5a1d5afb9",
+ "sha256:27d3f3b9e3406579a8af3a9f262f5339005dd25e0ecf3cf1559ff8a49ed5cbf2",
+ "sha256:2a2740aa9733d2e5b2dfb33639d98a64c3b0f24765fed86b0fd2aec07f6a0a08",
+ "sha256:4377e10b874e653fe96985c05feed2225c912e328c8a26541f7fc600fb9c637b",
+ "sha256:448ebb1b3bf64c0267d6b09a7cba26b5ae61b6d2dbabff7c91b660c7eccf2bdb",
+ "sha256:50e86c076611212ca62e5a59f518edafe0c0730f7d9195fec718da1a5c2bb1fc",
+ "sha256:5734bdc0342aba9dfc6f04920988140fb41234db42381cf7ccba64169f9fe7ac",
+ "sha256:5ddd1dfa2be066595c1993165b4cae84b9866b12339d0c903db7f21a094324a3",
+ "sha256:64324f64f90a9e4ef732be0928be853eee378fd6a01be21a0a8469c4f2682c83",
+ "sha256:6ae6c680f3ebf1cf7ad1d7748868b39d9f900836df774c453c11c5440bc15b36",
+ "sha256:6d7593a705d662be5bfe24111af14763016765f43cb6923ed86223f965f52387",
+ "sha256:8cac8790a6b1ddf88640a9267ee67b1aee7a57dfa2d2dd33999d080bc8ee3a0f",
+ "sha256:8ece138c3a16db8c1ad38f52eb32be6086cc72f403150a79336eb2045723a1ad",
+ "sha256:9eeb7d1d04b117ac0d38719915ae169aa6b61fca227b0b7d198d43728f0c879c",
+ "sha256:a09f98011236a419ee3f49cedc9ef27d7a1651df07810ae430a6b06576e0b414",
+ "sha256:a5d897c14513590a85774180be713f692df6fa8ecf6483e561a6d47309566f37",
+ "sha256:ad6f2ff5b1989a4899bf89800a671d71b1612e5ff40866d1f4d8bcf48d4e5764",
+ "sha256:c42c4b73121caf0ed6cd795512c9c09c52a7287b04d105d112068c1736d7c753",
+ "sha256:cb1017eec5257e9ac6209ac172058c430e834d5d2bc21961dceeb79d111e5909",
+ "sha256:d6c7bb82883680e168b55b49c70af29b84b84abb161cbac2800e8fcb6f2109b6",
+ "sha256:e452dc66e08a4ce642a961f134814258a082832c78c90351b75c41ad16f79f63",
+ "sha256:e5b6ed0f0b42317050c88022349d994fe72bfe35f5908617512cd8c8ef9da2a9",
+ "sha256:e9b30d4bd69498fc0c3fe9db5f62fffbb06b8eb9321f92cc970f2969be5e3949",
+ "sha256:ec149b90019852266fec2341ce1db513b843e496d5a8e8cdb5ced1923a92faab",
+ "sha256:edb01671b3caae1ca00881686003d16c2209e07b7ef8b7639f1867852b948f7c",
+ "sha256:f0d3929fe88ee1c155129ecd82f981b8856c5d97bcb0d5f23e9b4242e79d1de3",
+ "sha256:f29454410db6ef8126c83bd3c968d143304633d45dc57b51252afbd79d700893",
+ "sha256:fe45becb4c2f72a0907c1d0246ea6449fe7a9e2293bb0e11c4e9a32bb0930a15",
+ "sha256:fedbd128668ead37f33917820b704784aff695e0019309ad446a6d0b065b57e4"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==1.19.4"
+ },
+ "ocrmypdf": {
+ "hashes": [
+ "sha256:91e7394172cedb3be801a229dbd3d308fb5ae80cbc3a77879fa7954beea407b1",
+ "sha256:e550b8e884150accab7ea41f4a576b5844594cb5cbd6ed514fbf1206720343ad"
+ ],
+ "index": "pypi",
+ "version": "==11.3.4"
+ },
+ "pathtools": {
+ "hashes": [
+ "sha256:7c35c5421a39bb82e58018febd90e3b6e5db34c5443aaaf742b3f33d4655f1c0",
+ "sha256:d77d982475e87f32b82157a43b09f0a5ef3e66c1d8f3c7eb8d2580e783cd8202"
+ ],
+ "version": "==0.1.2"
+ },
+ "pathvalidate": {
+ "hashes": [
+ "sha256:1697c8ea71ff4c48e7aa0eda72fe4581404be8f41e51a17363ef682dd6824d35",
+ "sha256:32d30dbacb711c16bb188b12ce7e9a46b41785f50a12f64500f747480a4b6ee3"
+ ],
+ "index": "pypi",
+ "version": "==2.3.0"
+ },
+ "pdfminer.six": {
+ "hashes": [
+ "sha256:b9aac0ebeafb21c08bf65f2039f4b2c5f78a3449d0a41df711d72445649e952a",
+ "sha256:d78877ba8d8bf957f3bb636c4f73f4f6f30f56c461993877ac22c39c20837509"
+ ],
+ "markers": "python_version >= '3.4'",
+ "version": "==20201018"
+ },
+ "pdftotext": {
+ "hashes": [
+ "sha256:98aeb8b07a4127e1a30223bd933ef080bbd29aa88f801717ca6c5618380b8aa6"
+ ],
+ "index": "pypi",
+ "version": "==2.1.5"
+ },
+ "pikepdf": {
+ "hashes": [
+ "sha256:0829bd5dacd73bb4a37e7575bae523f49603479755563c92ddb55c206700cab1",
+ "sha256:0d2b631077cd6af6e4d1b396208020705842610a6f13fab489d5f9c47916baa2",
+ "sha256:21c98af08fae4ac9fbcad02b613b6768a4ca300fda4cba867f4a4b6f73c2d04b",
+ "sha256:2240372fed30124ddc35b0c15a613f2b687a426ea2f150091e0a0c58cca7a495",
+ "sha256:2a97f5f1403e058d217d7f6861cf51fca200c5687bce0d052f5f2fa89b5bfa22",
+ "sha256:3faaefca0ae80d19891acec8b0dd5e6235f59f2206d82375eb80d090285e9557",
+ "sha256:48ef45b64882901c0d69af3b85d16a19bd0f3e95b43e614fefb53521d8caf36c",
+ "sha256:5212fe41f2323fc7356ba67caa39737fe13080562cff37bcbb74a8094076c8d0",
+ "sha256:56859c32170663c57bd0658189ce44e180533eebe813853446cd6413810be9eb",
+ "sha256:5f8fd1cb3478c5534222018aca24fbbd2bc74460c899bda988ec76722c13caa9",
+ "sha256:74300a32c41b3d578772f6933f23a88b19f74484185e71e5225ce2f7ea5aea78",
+ "sha256:8cbc946bdd217148f4a9c029fcea62f4ae0f67d5346de4c865f4718cd0ddc37f",
+ "sha256:9ceefd30076f732530cf84a1be2ecb2fa9931af932706ded760a6d37c73b96ad",
+ "sha256:ad69c170fda41b07a4c6b668a3128e7a759f50d9aebcfcde0ccff1358abe0423",
+ "sha256:b715fe182189fb6870fab5b0383bb2fb278c88c46eade346b0f4c1ed8818c09d",
+ "sha256:bb01ecf95083ffcb9ad542dc5342ccc1059e46f1395fd966629d36d9cc766b4a",
+ "sha256:bd6328547219cf48cefb4e0a1bc54442910594de1c5a5feae847d9ff3c629031",
+ "sha256:edb128379bb1dea76b5bdbdacf5657a6e4754bacc2049640762725590d8ed905",
+ "sha256:f8e687900557fcd4c51b4e72b9e337fdae9e2c81049d1d80b624bb2e88b5769d",
+ "sha256:fe0ca120e3347c851c34a91041d574f3c588d832023906d8ae18d66d042e8a52",
+ "sha256:fe8e0152672f24d8bfdecc725f97e9013f2de1b41849150959526ca3562bd3ef"
+ ],
+ "index": "pypi",
+ "version": "==2.2.0"
+ },
+ "pillow": {
+ "hashes": [
+ "sha256:006de60d7580d81f4a1a7e9f0173dc90a932e3905cc4d47ea909bc946302311a",
+ "sha256:0a2e8d03787ec7ad71dc18aec9367c946ef8ef50e1e78c71f743bc3a770f9fae",
+ "sha256:0eeeae397e5a79dc088d8297a4c2c6f901f8fb30db47795113a4a605d0f1e5ce",
+ "sha256:11c5c6e9b02c9dac08af04f093eb5a2f84857df70a7d4a6a6ad461aca803fb9e",
+ "sha256:2fb113757a369a6cdb189f8df3226e995acfed0a8919a72416626af1a0a71140",
+ "sha256:4b0ef2470c4979e345e4e0cc1bbac65fda11d0d7b789dbac035e4c6ce3f98adb",
+ "sha256:59e903ca800c8cfd1ebe482349ec7c35687b95e98cefae213e271c8c7fffa021",
+ "sha256:5a3342d34289715928c914ee7f389351eb37fa4857caa9297fc7948f2ed3e53d",
+ "sha256:5abd653a23c35d980b332bc0431d39663b1709d64142e3652890df4c9b6970f6",
+ "sha256:5f9403af9c790cc18411ea398a6950ee2def2a830ad0cfe6dc9122e6d528b302",
+ "sha256:6b4a8fd632b4ebee28282a9fef4c341835a1aa8671e2770b6f89adc8e8c2703c",
+ "sha256:6c1aca8231625115104a06e4389fcd9ec88f0c9befbabd80dc206c35561be271",
+ "sha256:795e91a60f291e75de2e20e6bdd67770f793c8605b553cb6e4387ce0cb302e09",
+ "sha256:7ba0ba61252ab23052e642abdb17fd08fdcfdbbf3b74c969a30c58ac1ade7cd3",
+ "sha256:7c9401e68730d6c4245b8e361d3d13e1035cbc94db86b49dc7da8bec235d0015",
+ "sha256:81f812d8f5e8a09b246515fac141e9d10113229bc33ea073fec11403b016bcf3",
+ "sha256:895d54c0ddc78a478c80f9c438579ac15f3e27bf442c2a9aa74d41d0e4d12544",
+ "sha256:8de332053707c80963b589b22f8e0229f1be1f3ca862a932c1bcd48dafb18dd8",
+ "sha256:92c882b70a40c79de9f5294dc99390671e07fc0b0113d472cbea3fde15db1792",
+ "sha256:95edb1ed513e68bddc2aee3de66ceaf743590bf16c023fb9977adc4be15bd3f0",
+ "sha256:b63d4ff734263ae4ce6593798bcfee6dbfb00523c82753a3a03cbc05555a9cc3",
+ "sha256:bd7bf289e05470b1bc74889d1466d9ad4a56d201f24397557b6f65c24a6844b8",
+ "sha256:cc3ea6b23954da84dbee8025c616040d9aa5eaf34ea6895a0a762ee9d3e12e11",
+ "sha256:cc9ec588c6ef3a1325fa032ec14d97b7309db493782ea8c304666fb10c3bd9a7",
+ "sha256:d3d07c86d4efa1facdf32aa878bd508c0dc4f87c48125cc16b937baa4e5b5e11",
+ "sha256:d8a96747df78cda35980905bf26e72960cba6d355ace4780d4bdde3b217cdf1e",
+ "sha256:e38d58d9138ef972fceb7aeec4be02e3f01d383723965bfcef14d174c8ccd039",
+ "sha256:eb472586374dc66b31e36e14720747595c2b265ae962987261f044e5cce644b5",
+ "sha256:fbd922f702582cb0d71ef94442bfca57624352622d75e3be7a1e7e9360b07e72"
+ ],
+ "index": "pypi",
+ "version": "==8.0.1"
+ },
+ "pluggy": {
+ "hashes": [
+ "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
+ "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==0.13.1"
+ },
+ "psycopg2-binary": {
+ "hashes": [
+ "sha256:0deac2af1a587ae12836aa07970f5cb91964f05a7c6cdb69d8425ff4c15d4e2c",
+ "sha256:0e4dc3d5996760104746e6cfcdb519d9d2cd27c738296525d5867ea695774e67",
+ "sha256:11b9c0ebce097180129e422379b824ae21c8f2a6596b159c7659e2e5a00e1aa0",
+ "sha256:15978a1fbd225583dd8cdaf37e67ccc278b5abecb4caf6b2d6b8e2b948e953f6",
+ "sha256:1fabed9ea2acc4efe4671b92c669a213db744d2af8a9fc5d69a8e9bc14b7a9db",
+ "sha256:2dac98e85565d5688e8ab7bdea5446674a83a3945a8f416ad0110018d1501b94",
+ "sha256:42ec1035841b389e8cc3692277a0bd81cdfe0b65d575a2c8862cec7a80e62e52",
+ "sha256:6422f2ff0919fd720195f64ffd8f924c1395d30f9a495f31e2392c2efafb5056",
+ "sha256:6a32f3a4cb2f6e1a0b15215f448e8ce2da192fd4ff35084d80d5e39da683e79b",
+ "sha256:7312e931b90fe14f925729cde58022f5d034241918a5c4f9797cac62f6b3a9dd",
+ "sha256:7d92a09b788cbb1aec325af5fcba9fed7203897bbd9269d5691bb1e3bce29550",
+ "sha256:833709a5c66ca52f1d21d41865a637223b368c0ee76ea54ca5bad6f2526c7679",
+ "sha256:89705f45ce07b2dfa806ee84439ec67c5d9a0ef20154e0e475e2b2ed392a5b83",
+ "sha256:8cd0fb36c7412996859cb4606a35969dd01f4ea34d9812a141cd920c3b18be77",
+ "sha256:950bc22bb56ee6ff142a2cb9ee980b571dd0912b0334aa3fe0fe3788d860bea2",
+ "sha256:a0c50db33c32594305b0ef9abc0cb7db13de7621d2cadf8392a1d9b3c437ef77",
+ "sha256:a0eb43a07386c3f1f1ebb4dc7aafb13f67188eab896e7397aa1ee95a9c884eb2",
+ "sha256:aaa4213c862f0ef00022751161df35804127b78adf4a2755b9f991a507e425fd",
+ "sha256:ac0c682111fbf404525dfc0f18a8b5f11be52657d4f96e9fcb75daf4f3984859",
+ "sha256:ad20d2eb875aaa1ea6d0f2916949f5c08a19c74d05b16ce6ebf6d24f2c9f75d1",
+ "sha256:b4afc542c0ac0db720cf516dd20c0846f71c248d2b3d21013aa0d4ef9c71ca25",
+ "sha256:b8a3715b3c4e604bcc94c90a825cd7f5635417453b253499664f784fc4da0152",
+ "sha256:ba28584e6bca48c59eecbf7efb1576ca214b47f05194646b081717fa628dfddf",
+ "sha256:ba381aec3a5dc29634f20692349d73f2d21f17653bda1decf0b52b11d694541f",
+ "sha256:bd1be66dde2b82f80afb9459fc618216753f67109b859a361cf7def5c7968729",
+ "sha256:c2507d796fca339c8fb03216364cca68d87e037c1f774977c8fc377627d01c71",
+ "sha256:cec7e622ebc545dbb4564e483dd20e4e404da17ae07e06f3e780b2dacd5cee66",
+ "sha256:d14b140a4439d816e3b1229a4a525df917d6ea22a0771a2a78332273fd9528a4",
+ "sha256:d1b4ab59e02d9008efe10ceabd0b31e79519da6fb67f7d8e8977118832d0f449",
+ "sha256:d5227b229005a696cc67676e24c214740efd90b148de5733419ac9aaba3773da",
+ "sha256:d9f3a909b59ac4a3ca9beb77716f4bce627276edb039a71d4e9ec4b7548536a0",
+ "sha256:e1f57aa70d3f7cc6947fd88636a481638263ba04a742b4a37dd25c373e41491a",
+ "sha256:e74a55f6bad0e7d3968399deb50f61f4db1926acf4a6d83beaaa7df986f48b1c",
+ "sha256:e7f5a465c6431c0ad8d4e69603ee3306e521a09d3c6af76a16bdb62946bdddf0",
+ "sha256:e82aba2188b9ba309fd8e271702bd0d0fc9148ae3150532bbb474f4590039ffb",
+ "sha256:ee69dad2c7155756ad114c02db06002f4cded41132cc51378e57aad79cc8e4f4",
+ "sha256:f5ab93a2cb2d8338b1674be43b442a7f544a0971da062a5da774ed40587f18f5"
+ ],
+ "index": "pypi",
+ "version": "==2.8.6"
+ },
+ "pycparser": {
+ "hashes": [
+ "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0",
+ "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==2.20"
+ },
+ "python-dateutil": {
+ "hashes": [
+ "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c",
+ "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"
+ ],
+ "index": "pypi",
+ "version": "==2.8.1"
+ },
+ "python-dotenv": {
+ "hashes": [
+ "sha256:0c8d1b80d1a1e91717ea7d526178e3882732420b03f08afea0406db6402e220e",
+ "sha256:587825ed60b1711daea4832cf37524dfd404325b7db5e25ebe88c495c9f807a0"
+ ],
+ "index": "pypi",
+ "version": "==0.15.0"
+ },
+ "python-gnupg": {
+ "hashes": [
+ "sha256:3aa0884b3bd414652c2385b9df39e7b87272c2eca1b8fcc3089bc9e58652019a",
+ "sha256:cba3566e8a8fb7bb417d6897a6e17bfc7f9371052e57eb0057783c07d762a679"
+ ],
+ "index": "pypi",
+ "version": "==0.4.6"
+ },
+ "python-levenshtein": {
+ "hashes": [
+ "sha256:033a11de5e3d19ea25c9302d11224e1a1898fe5abd23c61c7c360c25195e3eb1",
+ "sha256:15e26882728c29ccdf74cfc6ac4b49fc22c08b44d152348cb0eb1ec4f3dbf9df",
+ "sha256:3df5e5eb144570ecf5ad38864a2393068798328c7f05e7b167a49391d36a2db1",
+ "sha256:7f049b3ddc4b525bd469febafb98bf5202f789b722e0e4ccbec2ffbe8c07d7b4"
+ ],
+ "index": "pypi",
+ "version": "==0.12.0"
+ },
+ "python-magic": {
+ "hashes": [
+ "sha256:356efa93c8899047d1eb7d3eb91e871ba2f5b1376edbaf4cc305e3c872207355",
+ "sha256:b757db2a5289ea3f1ced9e60f072965243ea43a2221430048fd8cacab17be0ce"
+ ],
+ "index": "pypi",
+ "version": "==0.4.18"
+ },
+ "pytz": {
+ "hashes": [
+ "sha256:3e6b7dd2d1e0a59084bcee14a17af60c5c562cdc16d828e8eba2e683d3a7e268",
+ "sha256:5c55e189b682d420be27c6995ba6edce0c0a77dd67bfbe2ae6607134d5851ffd"
+ ],
+ "version": "==2020.4"
+ },
+ "redis": {
+ "hashes": [
+ "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2",
+ "sha256:3f1c7f166fa6c803613eec222224848a80f5e5b9c6af3aa82461506643034a7a",
+ "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24"
+ ],
+ "index": "pypi",
+ "version": "==3.5.3"
+ },
+ "regex": {
+ "hashes": [
+ "sha256:02951b7dacb123d8ea6da44fe45ddd084aa6777d4b2454fa0da61d569c6fa538",
+ "sha256:0d08e71e70c0237883d0bef12cad5145b84c3705e9c6a588b2a9c7080e5af2a4",
+ "sha256:1862a9d9194fae76a7aaf0150d5f2a8ec1da89e8b55890b1786b8f88a0f619dc",
+ "sha256:1ab79fcb02b930de09c76d024d279686ec5d532eb814fd0ed1e0051eb8bd2daa",
+ "sha256:1fa7ee9c2a0e30405e21031d07d7ba8617bc590d391adfc2b7f1e8b99f46f444",
+ "sha256:262c6825b309e6485ec2493ffc7e62a13cf13fb2a8b6d212f72bd53ad34118f1",
+ "sha256:2a11a3e90bd9901d70a5b31d7dd85114755a581a5da3fc996abfefa48aee78af",
+ "sha256:2c99e97d388cd0a8d30f7c514d67887d8021541b875baf09791a3baad48bb4f8",
+ "sha256:3128e30d83f2e70b0bed9b2a34e92707d0877e460b402faca908c6667092ada9",
+ "sha256:38c8fd190db64f513fe4e1baa59fed086ae71fa45083b6936b52d34df8f86a88",
+ "sha256:3bddc701bdd1efa0d5264d2649588cbfda549b2899dc8d50417e47a82e1387ba",
+ "sha256:4902e6aa086cbb224241adbc2f06235927d5cdacffb2425c73e6570e8d862364",
+ "sha256:49cae022fa13f09be91b2c880e58e14b6da5d10639ed45ca69b85faf039f7a4e",
+ "sha256:56e01daca75eae420bce184edd8bb341c8eebb19dd3bce7266332258f9fb9dd7",
+ "sha256:5862975b45d451b6db51c2e654990c1820523a5b07100fc6903e9c86575202a0",
+ "sha256:6a8ce43923c518c24a2579fda49f093f1397dad5d18346211e46f134fc624e31",
+ "sha256:6c54ce4b5d61a7129bad5c5dc279e222afd00e721bf92f9ef09e4fae28755683",
+ "sha256:6e4b08c6f8daca7d8f07c8d24e4331ae7953333dbd09c648ed6ebd24db5a10ee",
+ "sha256:717881211f46de3ab130b58ec0908267961fadc06e44f974466d1887f865bd5b",
+ "sha256:749078d1eb89484db5f34b4012092ad14b327944ee7f1c4f74d6279a6e4d1884",
+ "sha256:7913bd25f4ab274ba37bc97ad0e21c31004224ccb02765ad984eef43e04acc6c",
+ "sha256:7a25fcbeae08f96a754b45bdc050e1fb94b95cab046bf56b016c25e9ab127b3e",
+ "sha256:80ef188c0e47a6c964eed71c55a73c245f8daf9f0a4a9d804e91275afb468ca4",
+ "sha256:83d6b356e116ca119db8e7c6fc2983289d87b27b3fac238cfe5dca529d884562",
+ "sha256:842fb985b2b99a82a2b145b6bbd588c5f5cfd83693402920fcb985d515794666",
+ "sha256:8b882a78c320478b12ff024e81dc7d43c1462aa4a3341c754ee65d857a521f85",
+ "sha256:8f6a2229e8ad946e36815f2a03386bb8353d4bde368fdf8ca5f0cb97264d3b5c",
+ "sha256:9801c4c1d9ae6a70aeb2128e5b4b68c45d4f0af0d1535500884d644fa9b768c6",
+ "sha256:a15f64ae3a027b64496a71ab1f722355e570c3fac5ba2801cafce846bf5af01d",
+ "sha256:a3d748383762e56337c39ab35c6ed4deb88df5326f97a38946ddd19028ecce6b",
+ "sha256:a63f1a07932c9686d2d416fb295ec2c01ab246e89b4d58e5fa468089cab44b70",
+ "sha256:b2b1a5ddae3677d89b686e5c625fc5547c6e492bd755b520de5332773a8af06b",
+ "sha256:b2f4007bff007c96a173e24dcda236e5e83bde4358a557f9ccf5e014439eae4b",
+ "sha256:baf378ba6151f6e272824b86a774326f692bc2ef4cc5ce8d5bc76e38c813a55f",
+ "sha256:bafb01b4688833e099d79e7efd23f99172f501a15c44f21ea2118681473fdba0",
+ "sha256:bba349276b126947b014e50ab3316c027cac1495992f10e5682dc677b3dfa0c5",
+ "sha256:c084582d4215593f2f1d28b65d2a2f3aceff8342aa85afd7be23a9cad74a0de5",
+ "sha256:d1ebb090a426db66dd80df8ca85adc4abfcbad8a7c2e9a5ec7513ede522e0a8f",
+ "sha256:d2d8ce12b7c12c87e41123997ebaf1a5767a5be3ec545f64675388970f415e2e",
+ "sha256:e32f5f3d1b1c663af7f9c4c1e72e6ffe9a78c03a31e149259f531e0fed826512",
+ "sha256:e3faaf10a0d1e8e23a9b51d1900b72e1635c2d5b0e1bea1c18022486a8e2e52d",
+ "sha256:f7d29a6fc4760300f86ae329e3b6ca28ea9c20823df123a2ea8693e967b29917",
+ "sha256:f8f295db00ef5f8bae530fc39af0b40486ca6068733fb860b42115052206466f"
+ ],
+ "version": "==2020.11.13"
+ },
+ "reportlab": {
+ "hashes": [
+ "sha256:0008b5baa39d7e3a8132c4b47ecae88d6858ad386518e754e5e7b8025ee4722b",
+ "sha256:0ad5a540c336941272fe161ef3a9830da3d4b3a65a195531cebd3cad5db58b2a",
+ "sha256:0c965a5691686d746f558ee1c52aa9c63a01a0e13cba61ffc661573948e32f61",
+ "sha256:0fd568fa5615ae99f76289c52ff230207852ee942d4934f6c893c93d2a79544e",
+ "sha256:1117d905a3404c696869c7aabec9454b43ed6acbbc73f9256c6fcea23e7ae93e",
+ "sha256:1ea7c388e91ad9d823655ad6a13751ff67e8a0e7cf4065cf051b4c931cdd9450",
+ "sha256:26c0ee8f62652cc7fcdc47a1cb3b34775a4d625738025c1a7edb8718bda5a315",
+ "sha256:368c5b3fc3d5a541cb9dcacefa563fdb445365f517e3cbf64b4326631d1cf13c",
+ "sha256:451d42fdcdd7d84587d6d9c8f5d9a7d0e997305efb606705063ca1fe8bcca551",
+ "sha256:47394acba4da8e56ef8e55d8eb483b868521696ba49ab0f0fcf8a1a4a5ac6e49",
+ "sha256:51b16e297f7b937fc530dd151e4b38f1d305b01c9aa10657bc32a5d2901b8ad7",
+ "sha256:51c0cdcf606ded0a7b4b50050400f25125ea797fbfc3c817135993b38f8b764e",
+ "sha256:55c672c579618843e0fd00140fb71f1ffebc4f1c542ac385c4f4999f2f5398d9",
+ "sha256:5c34a96ecfbf595caf16178a06abcd26a5f8720e01fe1285d4c97333382cfaeb",
+ "sha256:61aa89a00754b18c4f2956b8bff831f1fd3affef6476dc63462d92211941605e",
+ "sha256:62234d29c97279917903e4587faf240a5dea4617be250db55386ff268eb5a7c5",
+ "sha256:670f2a8dcc23bf798c39b95c64bf76ee387549b962f76783670821978a226663",
+ "sha256:69387f171f6c7b55109caa6d061b17a18f2f9e724a0212c07cd692aeb369dd19",
+ "sha256:6c5c8871b659f7c2975382d7b61f3c182701fa9eb62cf649c3c73ba8fc5e2595",
+ "sha256:80139ceb3a568f5be908094f1701fd05391b71425e8b69aaed0d30db647ca2aa",
+ "sha256:80661a76d0019b5e2c315ccd3bc7093d754067d6142b36a3a0ec4f416073d23b",
+ "sha256:85a2236f324ae336da7f4b183fa99bed261bcc00ac1255ee91a504e68b086d00",
+ "sha256:89a3acd98bd4478d6bbc5cb32e0665ea546c98bff8b58d5e1014659daa6ef75a",
+ "sha256:8a39119fcab146bde41fd1c6d148f9ee1e2cca10c6f9c2b7eb4dd710a3a2c6ac",
+ "sha256:9c31c2526401da6cc92018f68483f2aac0a731cb98435445ea4b72d46b438c84",
+ "sha256:9e8ae1c3b8a1697147c5c97f00d66ab1c54d88c4615b0cdd9b1a667d7baf3eb7",
+ "sha256:a479c38ab2b997ce05d3bef906783ac20cf4cb224a154e80c9018c5e4d943a35",
+ "sha256:a79aab8d069543d5085d58260f18705a08acd92a4501a41261913fddc2137d46",
+ "sha256:b0a8314383de853599ca531dfe55eaa49bb8d6b0bb663b2f8479b7a0f3385ea2",
+ "sha256:b3d9926e64bd8008007b2d9819d7b30179b069ce95431d5060f71afc36885389",
+ "sha256:c2a9a77ce4f25ffb52d705be82a9f41b47f6b0da23870ebc3587709e7242da30",
+ "sha256:c578dd0799f70fb577474cd383f035c6e1057e4fe837278113f9cfa6eee4b076",
+ "sha256:c5abd9d0023ad20030524ab0d5fa39d77aed025519b1fa426304ab2dd0328b89",
+ "sha256:ced96125525ba21311e9512adf391170b9e149f89e27e45b06ff07b70f97a0b2",
+ "sha256:d692fb88d6ef5e75242b00009b54953a0425eaa8bd3a36db9db8b396785e1f57",
+ "sha256:d70c2104286459658e61388af9eee838b612986bd8a36e1d21ba36152983ac15",
+ "sha256:de47c65c10ac6f0d2addb28f1b1657b1c707aca014d09d01b3b728cf19e8f791",
+ "sha256:e6e7592527791841db0820a72c6afae52655a05b0b6d4df184fd2bafe82ee1ee",
+ "sha256:e8a7e95ee6ea5566291b59ede5b9fadce809dca43ebfbfe11e3ff3d6492c6f0e",
+ "sha256:f041759138b3a95508c4281b3db3bf9bb28636d84c554272a58a5ca7c9f9bbf4",
+ "sha256:f39c7fc1fa2e4a1d9747a3effd70731a9d0e9eb5738247fa089c059eff19d43e",
+ "sha256:f65ac89ee0ba569f5279360eae08783f7f2e95c9810a9846c957fbd5950f4896"
+ ],
+ "version": "==3.5.56"
+ },
+ "scikit-learn": {
+ "hashes": [
+ "sha256:090bbf144fd5823c1f2efa3e1a9bf180295b24294ca8f478e75b40ed54f8036e",
+ "sha256:0a127cc70990d4c15b1019680bfedc7fec6c23d14d3719fdf9b64b22d37cdeca",
+ "sha256:0d39748e7c9669ba648acf40fb3ce96b8a07b240db6888563a7cb76e05e0d9cc",
+ "sha256:1b8a391de95f6285a2f9adffb7db0892718950954b7149a70c783dc848f104ea",
+ "sha256:20766f515e6cd6f954554387dfae705d93c7b544ec0e6c6a5d8e006f6f7ef480",
+ "sha256:2aa95c2f17d2f80534156215c87bee72b6aa314a7f8b8fe92a2d71f47280570d",
+ "sha256:5ce7a8021c9defc2b75620571b350acc4a7d9763c25b7593621ef50f3bd019a2",
+ "sha256:6c28a1d00aae7c3c9568f61aafeaad813f0f01c729bee4fd9479e2132b215c1d",
+ "sha256:7671bbeddd7f4f9a6968f3b5442dac5f22bf1ba06709ef888cc9132ad354a9ab",
+ "sha256:914ac2b45a058d3f1338d7736200f7f3b094857758895f8667be8a81ff443b5b",
+ "sha256:98508723f44c61896a4e15894b2016762a55555fbf09365a0bb1870ecbd442de",
+ "sha256:a64817b050efd50f9abcfd311870073e500ae11b299683a519fbb52d85e08d25",
+ "sha256:cb3e76380312e1f86abd20340ab1d5b3cc46a26f6593d3c33c9ea3e4c7134028",
+ "sha256:d0dcaa54263307075cb93d0bee3ceb02821093b1b3d25f66021987d305d01dce",
+ "sha256:d9a1ce5f099f29c7c33181cc4386660e0ba891b21a60dc036bf369e3a3ee3aec",
+ "sha256:da8e7c302003dd765d92a5616678e591f347460ac7b53e53d667be7dfe6d1b10",
+ "sha256:daf276c465c38ef736a79bd79fc80a249f746bcbcae50c40945428f7ece074f8"
+ ],
+ "index": "pypi",
+ "version": "==0.23.2"
+ },
+ "scipy": {
+ "hashes": [
+ "sha256:168c45c0c32e23f613db7c9e4e780bc61982d71dcd406ead746c7c7c2f2004ce",
+ "sha256:213bc59191da2f479984ad4ec39406bf949a99aba70e9237b916ce7547b6ef42",
+ "sha256:25b241034215247481f53355e05f9e25462682b13bd9191359075682adcd9554",
+ "sha256:2c872de0c69ed20fb1a9b9cf6f77298b04a26f0b8720a5457be08be254366c6e",
+ "sha256:3397c129b479846d7eaa18f999369a24322d008fac0782e7828fa567358c36ce",
+ "sha256:368c0f69f93186309e1b4beb8e26d51dd6f5010b79264c0f1e9ca00cd92ea8c9",
+ "sha256:3d5db5d815370c28d938cf9b0809dade4acf7aba57eaf7ef733bfedc9b2474c4",
+ "sha256:4598cf03136067000855d6b44d7a1f4f46994164bcd450fb2c3d481afc25dd06",
+ "sha256:4a453d5e5689de62e5d38edf40af3f17560bfd63c9c5bd228c18c1f99afa155b",
+ "sha256:4f12d13ffbc16e988fa40809cbbd7a8b45bc05ff6ea0ba8e3e41f6f4db3a9e47",
+ "sha256:634568a3018bc16a83cda28d4f7aed0d803dd5618facb36e977e53b2df868443",
+ "sha256:65923bc3809524e46fb7eb4d6346552cbb6a1ffc41be748535aa502a2e3d3389",
+ "sha256:6b0ceb23560f46dd236a8ad4378fc40bad1783e997604ba845e131d6c680963e",
+ "sha256:8c8d6ca19c8497344b810b0b0344f8375af5f6bb9c98bd42e33f747417ab3f57",
+ "sha256:9ad4fcddcbf5dc67619379782e6aeef41218a79e17979aaed01ed099876c0e62",
+ "sha256:a254b98dbcc744c723a838c03b74a8a34c0558c9ac5c86d5561703362231107d",
+ "sha256:b03c4338d6d3d299e8ca494194c0ae4f611548da59e3c038813f1a43976cb437",
+ "sha256:b5e9d3e4474644915809d6aa1416ff20430a3ed9ae723a5d295da5ddb24985e2",
+ "sha256:cc1f78ebc982cd0602c9a7615d878396bec94908db67d4ecddca864d049112f2",
+ "sha256:d6d25c41a009e3c6b7e757338948d0076ee1dd1770d1c09ec131f11946883c54",
+ "sha256:d84cadd7d7998433334c99fa55bcba0d8b4aeff0edb123b2a1dfcface538e474",
+ "sha256:e360cb2299028d0b0d0f65a5c5e51fc16a335f1603aa2357c25766c8dab56938",
+ "sha256:e98d49a5717369d8241d6cf33ecb0ca72deee392414118198a8e5b4c35c56340",
+ "sha256:ed572470af2438b526ea574ff8f05e7f39b44ac37f712105e57fc4d53a6fb660",
+ "sha256:f87b39f4d69cf7d7529d7b1098cb712033b17ea7714aed831b95628f483fd012",
+ "sha256:fa789583fc94a7689b45834453fec095245c7e69c58561dc159b5d5277057e4c"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==1.5.4"
+ },
+ "six": {
+ "hashes": [
+ "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
+ "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
+ ],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==1.15.0"
+ },
+ "sortedcontainers": {
+ "hashes": [
+ "sha256:37257a32add0a3ee490bb170b599e93095eed89a55da91fa9f48753ea12fd73f",
+ "sha256:59cc937650cf60d677c16775597c89a960658a09cf7c1a668f86e1e4464b10a1"
+ ],
+ "version": "==2.3.0"
+ },
+ "sqlparse": {
+ "hashes": [
+ "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0",
+ "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==0.4.1"
+ },
+ "threadpoolctl": {
+ "hashes": [
+ "sha256:38b74ca20ff3bb42caca8b00055111d74159ee95c4370882bbff2b93d24da725",
+ "sha256:ddc57c96a38beb63db45d6c159b5ab07b6bced12c45a1f07b2b92f272aebfa6b"
+ ],
+ "markers": "python_version >= '3.5'",
+ "version": "==2.1.0"
+ },
+ "tqdm": {
+ "hashes": [
+ "sha256:38b658a3e4ecf9b4f6f8ff75ca16221ae3378b2e175d846b6b33ea3a20852cf5",
+ "sha256:d4f413aecb61c9779888c64ddf0c62910ad56dcbe857d8922bb505d4dbff0df1"
+ ],
+ "index": "pypi",
+ "version": "==4.54.1"
+ },
+ "tzlocal": {
+ "hashes": [
+ "sha256:643c97c5294aedc737780a49d9df30889321cbe1204eac2c2ec6134035a92e44",
+ "sha256:e2cb6c6b5b604af38597403e9852872d7f534962ae2954c7f35efcb1ccacf4a4"
+ ],
+ "version": "==2.1"
+ },
+ "watchdog": {
+ "hashes": [
+ "sha256:3caefdcc8f06a57fdc5ef2d22aa7c0bfda4f55e71a0bee74cbf3176d97536ef3",
+ "sha256:e38bffc89b15bafe2a131f0e1c74924cf07dcec020c2e0a26cccd208831fcd43"
+ ],
+ "index": "pypi",
+ "version": "==0.10.4"
+ },
+ "wcwidth": {
+ "hashes": [
+ "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784",
+ "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"
+ ],
+ "version": "==0.2.5"
+ },
+ "whitenoise": {
+ "hashes": [
+ "sha256:05ce0be39ad85740a78750c86a93485c40f08ad8c62a6006de0233765996e5c7",
+ "sha256:05d00198c777028d72d8b0bbd234db605ef6d60e9410125124002518a48e515d"
+ ],
+ "index": "pypi",
+ "version": "==5.2.0"
+ },
+ "whoosh": {
+ "hashes": [
+ "sha256:7ca5633dbfa9e0e0fa400d3151a8a0c4bec53bd2ecedc0a67705b17565c31a83",
+ "sha256:aa39c3c3426e3fd107dcb4bde64ca1e276a65a889d9085a6e4b54ba82420a852",
+ "sha256:e0857375f63e9041e03fedd5b7541f97cf78917ac1b6b06c1fcc9b45375dda69"
+ ],
+ "index": "pypi",
+ "version": "==2.7.4"
+ },
+ "zipp": {
+ "hashes": [
+ "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108",
+ "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"
+ ],
+ "markers": "python_version >= '3.6'",
+ "version": "==3.4.0"
+ }
+ },
+ "develop": {
"alabaster": {
"hashes": [
"sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359",
@@ -26,197 +838,107 @@
"sha256:37228cda29411948b422fae072f57e31d3396d2ee1c9783775980ee9c9990af6",
"sha256:58587dd4dc3daefad0487f6d9ae32b4542b185e1c36db6993290e7c41ca2b47c"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.5"
},
"appdirs": {
"hashes": [
- "sha256:9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92",
- "sha256:d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"
+ "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41",
+ "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"
],
- "version": "==1.4.3"
+ "version": "==1.4.4"
},
"attrs": {
"hashes": [
- "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
- "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
+ "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6",
+ "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"
],
- "version": "==19.3.0"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==20.3.0"
},
"babel": {
"hashes": [
- "sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38",
- "sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4"
+ "sha256:9d35c22fcc79893c3ecc85ac4a56cde1ecf3f19c540bba0922308a6c06ca6fa5",
+ "sha256:da031ab54472314f210b0adcff1588ee5d1d1d0ba4dbd07b94dba82bde791e05"
],
- "version": "==2.8.0"
- },
- "brotli": {
- "hashes": [
- "sha256:0538dc1744fd17c314d2adc409ea7d1b779783b89fd95bcfb0c2acc93a6ea5a7",
- "sha256:0970a47f471782912d7705160b2b0a9306e68e6fadf9cffcaeb42d8f0951e26c",
- "sha256:113f51658e6fe548dce4b3749f6ef6c24de4184ba9c10a909cbee4261c2a5da0",
- "sha256:1e1aa9c4d1558889f42749c8baf846007953bfd32c8209230cf1cd1f5ef33495",
- "sha256:2f2f4f78f29ac4a45d15b3d9fc3fd9705e0ad313a44b129f6e1d0c6916bad0e2",
- "sha256:3269f6de1dd150fd0cce1c158b61ff5ac06d627fd3ae9c6ea03aed26fbbff7ea",
- "sha256:3f4a1f6240916c7984c7f2542786710f622992508dafee0b1714e6d340fb9ffd",
- "sha256:50dd9ad2a2bb12da4e9002a438672d182f98e546e99952de80280a1e1729664f",
- "sha256:5519a4b01b1a4f965083cbfa2ef2b9774c5a5f352341c47b50776ad109423d72",
- "sha256:5eb27722d320370315971c427eb8aa7cc0791f2a458840d357ac653bd0ad3a14",
- "sha256:5f06b4d5b6f58e5b5c220c2f23cad034dc5efa51b01fde2351ced1605bd980e2",
- "sha256:71ceee286ea7ec613f1c36f1c6181864a6ca24ebb55e371276f33d6af8742834",
- "sha256:72848d25a5f9e736db4af4512e0c3feecc094d57d241f8f1ae959115a2c39756",
- "sha256:743001bca75f4a6b4454be3510feca46f9d61a0c782a9bc2bc684bdb245e279e",
- "sha256:7ac98c71a15648fd11bc1f32608b6110e396121280790082e32b9a3109048bc6",
- "sha256:9d1c2dd27a1083fefd05b1b2f8df4a6bc2aaa6c21dd82cd41c8ae5e7c23a87f8",
- "sha256:a13ce9b419fe9f277c63f700efb0e444331509d1881b5610d2ba7e9080606967",
- "sha256:a19ef0952b9d2803df88dff07f45a6c92d5676afb9b8d69cf32232d684036d11",
- "sha256:ad766ca8b8c1419b71a22756b45264f45725c86133dc80a7cbe30b6b78c75620",
- "sha256:ad7963f261988ee0883816b6b9f206f11461c9b3cb5cfbca0c9ab5adc406d395",
- "sha256:af0451e23016631a2f52925a10d738ac4a0f794ac315c30380b22efc0c90cbc6",
- "sha256:c16201060c5a3f8742e3deae759014251ac92f382f82bc2a41dc079ff18c3f24",
- "sha256:c43b202f65891861a9a336984a103de25de235f756de69e32db893156f767013",
- "sha256:c675c6cce4295cb1a692f3de7416aacace7314e064b94bc86e93aceefce7fd3e",
- "sha256:d17cec0b992b1434f5f9df9986563605a4d1b1acd5574c87fc2ac014bcbd3316",
- "sha256:dc91f6129953861a73d9a65c52a8dd682b561a9ebaf65283541645cab6489917",
- "sha256:e2f4cbd1760d2bf2f30e396c2301999aab0191aec031a6a8a04950b2f575a536",
- "sha256:f192e6d3556714105c10486bbd6d045e38a0c04d9da3cef21e0a8dfd8e162df4",
- "sha256:f775b07026af2b1b0b5a8b05e41571cdcf3a315a67df265d60af301656a5425b",
- "sha256:f969ec7f56ba9636679e69ca07fba548312ccaca37412ee823c7f413541ad7e0",
- "sha256:f9dc52cd70907aafb99a773b66b156f2f995c7a0d284397c487c8b71ddbef2f9",
- "sha256:f9ee88bb52352588ceb811d045b5c9bb1dc38927bc150fd156244f60ff3f59f1",
- "sha256:fc7212e36ebeb81aebf7949c92897b622490d7c0e333a479c0395591e7994600"
- ],
- "index": "pypi",
- "version": "==1.0.7"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==2.9.0"
},
"certifi": {
"hashes": [
- "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3",
- "sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"
+ "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c",
+ "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"
],
- "version": "==2019.11.28"
+ "version": "==2020.12.5"
},
"chardet": {
"hashes": [
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
],
+ "markers": "python_version >= '3.1'",
"version": "==3.0.4"
},
"coverage": {
"hashes": [
- "sha256:15cf13a6896048d6d947bf7d222f36e4809ab926894beb748fc9caa14605d9c3",
- "sha256:1daa3eceed220f9fdb80d5ff950dd95112cd27f70d004c7918ca6dfc6c47054c",
- "sha256:1e44a022500d944d42f94df76727ba3fc0a5c0b672c358b61067abb88caee7a0",
- "sha256:25dbf1110d70bab68a74b4b9d74f30e99b177cde3388e07cc7272f2168bd1477",
- "sha256:3230d1003eec018ad4a472d254991e34241e0bbd513e97a29727c7c2f637bd2a",
- "sha256:3dbb72eaeea5763676a1a1efd9b427a048c97c39ed92e13336e726117d0b72bf",
- "sha256:5012d3b8d5a500834783689a5d2292fe06ec75dc86ee1ccdad04b6f5bf231691",
- "sha256:51bc7710b13a2ae0c726f69756cf7ffd4362f4ac36546e243136187cfcc8aa73",
- "sha256:527b4f316e6bf7755082a783726da20671a0cc388b786a64417780b90565b987",
- "sha256:722e4557c8039aad9592c6a4213db75da08c2cd9945320220634f637251c3894",
- "sha256:76e2057e8ffba5472fd28a3a010431fd9e928885ff480cb278877c6e9943cc2e",
- "sha256:77afca04240c40450c331fa796b3eab6f1e15c5ecf8bf2b8bee9706cd5452fef",
- "sha256:7afad9835e7a651d3551eab18cbc0fdb888f0a6136169fbef0662d9cdc9987cf",
- "sha256:9bea19ac2f08672636350f203db89382121c9c2ade85d945953ef3c8cf9d2a68",
- "sha256:a8b8ac7876bc3598e43e2603f772d2353d9931709345ad6c1149009fd1bc81b8",
- "sha256:b0840b45187699affd4c6588286d429cd79a99d509fe3de0f209594669bb0954",
- "sha256:b26aaf69713e5674efbde4d728fb7124e429c9466aeaf5f4a7e9e699b12c9fe2",
- "sha256:b63dd43f455ba878e5e9f80ba4f748c0a2156dde6e0e6e690310e24d6e8caf40",
- "sha256:be18f4ae5a9e46edae3f329de2191747966a34a3d93046dbdf897319923923bc",
- "sha256:c312e57847db2526bc92b9bfa78266bfbaabac3fdcd751df4d062cd4c23e46dc",
- "sha256:c60097190fe9dc2b329a0eb03393e2e0829156a589bd732e70794c0dd804258e",
- "sha256:c62a2143e1313944bf4a5ab34fd3b4be15367a02e9478b0ce800cb510e3bbb9d",
- "sha256:cc1109f54a14d940b8512ee9f1c3975c181bbb200306c6d8b87d93376538782f",
- "sha256:cd60f507c125ac0ad83f05803063bed27e50fa903b9c2cfee3f8a6867ca600fc",
- "sha256:d513cc3db248e566e07a0da99c230aca3556d9b09ed02f420664e2da97eac301",
- "sha256:d649dc0bcace6fcdb446ae02b98798a856593b19b637c1b9af8edadf2b150bea",
- "sha256:d7008a6796095a79544f4da1ee49418901961c97ca9e9d44904205ff7d6aa8cb",
- "sha256:da93027835164b8223e8e5af2cf902a4c80ed93cb0909417234f4a9df3bcd9af",
- "sha256:e69215621707119c6baf99bda014a45b999d37602cb7043d943c76a59b05bf52",
- "sha256:ea9525e0fef2de9208250d6c5aeeee0138921057cd67fcef90fbed49c4d62d37",
- "sha256:fca1669d464f0c9831fd10be2eef6b86f5ebd76c724d1e0706ebdff86bb4adf0"
+ "sha256:0203acd33d2298e19b57451ebb0bed0ab0c602e5cf5a818591b4918b1f97d516",
+ "sha256:0f313707cdecd5cd3e217fc68c78a960b616604b559e9ea60cc16795c4304259",
+ "sha256:1c6703094c81fa55b816f5ae542c6ffc625fec769f22b053adb42ad712d086c9",
+ "sha256:1d44bb3a652fed01f1f2c10d5477956116e9b391320c94d36c6bf13b088a1097",
+ "sha256:280baa8ec489c4f542f8940f9c4c2181f0306a8ee1a54eceba071a449fb870a0",
+ "sha256:29a6272fec10623fcbe158fdf9abc7a5fa032048ac1d8631f14b50fbfc10d17f",
+ "sha256:2b31f46bf7b31e6aa690d4c7a3d51bb262438c6dcb0d528adde446531d0d3bb7",
+ "sha256:2d43af2be93ffbad25dd959899b5b809618a496926146ce98ee0b23683f8c51c",
+ "sha256:3188a7dfd96f734a7498f37cde6598b1e9c084f1ca68bc1aa04e88db31168ab6",
+ "sha256:381ead10b9b9af5f64646cd27107fb27b614ee7040bb1226f9c07ba96625cbb5",
+ "sha256:47a11bdbd8ada9b7ee628596f9d97fbd3851bd9999d398e9436bd67376dbece7",
+ "sha256:4d6a42744139a7fa5b46a264874a781e8694bb32f1d76d8137b68138686f1729",
+ "sha256:50691e744714856f03a86df3e2bff847c2acede4c191f9a1da38f088df342978",
+ "sha256:530cc8aaf11cc2ac7430f3614b04645662ef20c348dce4167c22d99bec3480e9",
+ "sha256:582ddfbe712025448206a5bc45855d16c2e491c2dd102ee9a2841418ac1c629f",
+ "sha256:63808c30b41f3bbf65e29f7280bf793c79f54fb807057de7e5238ffc7cc4d7b9",
+ "sha256:71b69bd716698fa62cd97137d6f2fdf49f534decb23a2c6fc80813e8b7be6822",
+ "sha256:7858847f2d84bf6e64c7f66498e851c54de8ea06a6f96a32a1d192d846734418",
+ "sha256:78e93cc3571fd928a39c0b26767c986188a4118edc67bc0695bc7a284da22e82",
+ "sha256:7f43286f13d91a34fadf61ae252a51a130223c52bfefb50310d5b2deb062cf0f",
+ "sha256:86e9f8cd4b0cdd57b4ae71a9c186717daa4c5a99f3238a8723f416256e0b064d",
+ "sha256:8f264ba2701b8c9f815b272ad568d555ef98dfe1576802ab3149c3629a9f2221",
+ "sha256:9342dd70a1e151684727c9c91ea003b2fb33523bf19385d4554f7897ca0141d4",
+ "sha256:9361de40701666b034c59ad9e317bae95c973b9ff92513dd0eced11c6adf2e21",
+ "sha256:9669179786254a2e7e57f0ecf224e978471491d660aaca833f845b72a2df3709",
+ "sha256:aac1ba0a253e17889550ddb1b60a2063f7474155465577caa2a3b131224cfd54",
+ "sha256:aef72eae10b5e3116bac6957de1df4d75909fc76d1499a53fb6387434b6bcd8d",
+ "sha256:bd3166bb3b111e76a4f8e2980fa1addf2920a4ca9b2b8ca36a3bc3dedc618270",
+ "sha256:c1b78fb9700fc961f53386ad2fd86d87091e06ede5d118b8a50dea285a071c24",
+ "sha256:c3888a051226e676e383de03bf49eb633cd39fc829516e5334e69b8d81aae751",
+ "sha256:c5f17ad25d2c1286436761b462e22b5020d83316f8e8fcb5deb2b3151f8f1d3a",
+ "sha256:c851b35fc078389bc16b915a0a7c1d5923e12e2c5aeec58c52f4aa8085ac8237",
+ "sha256:cb7df71de0af56000115eafd000b867d1261f786b5eebd88a0ca6360cccfaca7",
+ "sha256:cedb2f9e1f990918ea061f28a0f0077a07702e3819602d3507e2ff98c8d20636",
+ "sha256:e8caf961e1b1a945db76f1b5fa9c91498d15f545ac0ababbe575cfab185d3bd8",
+ "sha256:ef221855191457fffeb909d5787d1807800ab4d0111f089e6c93ee68f577634d"
],
- "version": "==5.0.3"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
+ "version": "==5.3"
},
"coveralls": {
"hashes": [
- "sha256:4b6bfc2a2a77b890f556bc631e35ba1ac21193c356393b66c84465c06218e135",
- "sha256:67188c7ec630c5f708c31552f2bcdac4580e172219897c4136504f14b823132f"
- ],
- "index": "pypi",
- "version": "==1.11.1"
- },
- "dateparser": {
- "hashes": [
- "sha256:983d84b5e3861cb0aa240cad07f12899bb10b62328aae188b9007e04ce37d665",
- "sha256:e1eac8ef28de69a554d5fcdb60b172d526d61924b1a40afbbb08df459a36006b"
- ],
- "index": "pypi",
- "version": "==0.7.2"
- },
- "distlib": {
- "hashes": [
- "sha256:2e166e231a26b36d6dfe35a48c4464346620f8645ed0ace01ee31822b288de21"
- ],
- "version": "==0.3.0"
- },
- "django": {
- "hashes": [
- "sha256:665457d4146bbd34ae9d2970fa3b37082d7b225b0671bfd24c337458f229db78",
- "sha256:bde46d4dbc410678e89bc95ea5d312dd6eb4c37d0fa0e19c9415cad94addf22f"
- ],
- "index": "pypi",
- "version": "==2.0.13"
- },
- "django-cors-headers": {
- "hashes": [
- "sha256:a5960addecc04527ab26617e51b8ed42f0adab4594b24bb0f3c33e2bd3857c3f",
- "sha256:a785b5f446f6635810776d9f5f5d23e6a2a2f728ea982648370afaf0dfdf2627"
- ],
- "index": "pypi",
- "version": "==3.2.1"
- },
- "django-crispy-forms": {
- "hashes": [
- "sha256:0afc0ba730f52a13c02bfbd0e1423af4577a337d73a8a0ef96f2cbbc5f345ffa",
- "sha256:2db711ce31f6f9ef42c16829cc3636e3819f97c1b22a3b706afed679bc417e88"
- ],
- "index": "pypi",
- "version": "==1.8.1"
- },
- "django-extensions": {
- "hashes": [
- "sha256:1a03c4e8bade575f8c2be6c76456f8a2be3f9b02ab9f47d3535afa9562dc0493",
- "sha256:2699cc1d6fb4bd393c0b5832fea4bc685f2ace5800b3c9ff222b2080f161ac04"
- ],
- "index": "pypi",
- "version": "==2.2.8"
- },
- "django-filter": {
- "hashes": [
- "sha256:558c727bce3ffa89c4a7a0b13bc8976745d63e5fd576b3a9a851650ef11c401b",
- "sha256:c3deb57f0dd7ff94d7dce52a047516822013e2b441bed472b722a317658cfd14"
+ "sha256:2301a19500b06649d2ec4f2858f9c69638d7699a4c63027c5d53daba666147cc",
+ "sha256:b990ba1f7bc4288e63340be0433698c1efe8217f78c689d254c2540af3d38617"
],
"index": "pypi",
"version": "==2.2.0"
},
- "djangoql": {
+ "distlib": {
"hashes": [
- "sha256:366293d7d4e416f9f7d6e2b98775c2129222fbb4dc660f3e6c7b9e35a3cf3fce"
+ "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb",
+ "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"
],
- "index": "pypi",
- "version": "==0.13.1"
- },
- "djangorestframework": {
- "hashes": [
- "sha256:05809fc66e1c997fd9a32ea5730d9f4ba28b109b9da71fccfa5ff241201fd0a4",
- "sha256:e782087823c47a26826ee5b6fa0c542968219263fb3976ec3c31edab23a4001f"
- ],
- "index": "pypi",
- "version": "==3.11.0"
+ "version": "==0.3.1"
},
"docopt": {
"hashes": [
+ "sha256:15fde8252aa9f2804171014d50d069ffbf42c7a50b7d74bcbb82bfd5700fcfc2",
"sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"
],
"version": "==0.6.2"
@@ -226,6 +948,7 @@
"sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af",
"sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
"version": "==0.16"
},
"execnet": {
@@ -233,90 +956,82 @@
"sha256:cacb9df31c9680ec5f95553976c4da484d407e85e41c83cb812aa014f0eddc50",
"sha256:d4efd397930c46415f62f8a31388d6be4f27a91d7550eb79bc64a756e0056547"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.7.1"
},
"factory-boy": {
"hashes": [
- "sha256:728df59b372c9588b83153facf26d3d28947fc750e8e3c95cefa9bed0e6394ee",
- "sha256:faf48d608a1735f0d0a3c9cbf536d64f9132b547dae7ba452c4d99a79e84a370"
+ "sha256:d8626622550c8ba31392f9e19fdbcef9f139cf1ad643c5923f20490a7b3e2e3d",
+ "sha256:ded73e49135c24bd4d3f45bf1eb168f8d290090f5cf4566b8df3698317dc9c08"
],
"index": "pypi",
- "version": "==2.12.0"
+ "version": "==3.1.0"
},
"faker": {
"hashes": [
- "sha256:047d4d1791bfb3756264da670d99df13d799bb36e7d88774b1585a82d05dbaec",
- "sha256:1b1a58961683b30c574520d0c739c4443e0ef6a185c04382e8cc888273dbebed"
+ "sha256:1fcb415562ee6e2395b041e85fa6901d4708d30b84d54015226fa754ed0822c3",
+ "sha256:e8beccb398ee9b8cc1a91d9295121d66512b6753b4846eb1e7370545d46b3311"
],
- "version": "==4.0.0"
+ "markers": "python_version >= '3.6'",
+ "version": "==5.0.1"
},
"filelock": {
"hashes": [
"sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59",
"sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"
],
+ "index": "pypi",
"version": "==3.0.12"
},
- "filemagic": {
- "hashes": [
- "sha256:e684359ef40820fe406f0ebc5bf8a78f89717bdb7fed688af68082d991d6dbf3"
- ],
- "index": "pypi",
- "version": "==1.6"
- },
- "fuzzywuzzy": {
- "extras": [
- "speedup"
- ],
- "hashes": [
- "sha256:3759bc6859daa0eecef8c82b45404bdac20c23f23136cf4c18b46b426bbc418f",
- "sha256:5b36957ccf836e700f4468324fa80ba208990385392e217be077d5cd738ae602"
- ],
- "index": "pypi",
- "version": "==0.15.0"
- },
- "gunicorn": {
- "hashes": [
- "sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626",
- "sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c"
- ],
- "index": "pypi",
- "version": "==20.0.4"
- },
"idna": {
"hashes": [
- "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
- "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
+ "sha256:4a57a6379512ade94fa99e2fa46d3cd0f2f553040548d0e2958c6ed90ee48226",
+ "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6",
+ "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"
],
- "version": "==2.8"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==2.10"
},
"imagesize": {
"hashes": [
"sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1",
"sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.2.0"
},
- "inotify-simple": {
+ "importlib-metadata": {
"hashes": [
- "sha256:db69f2d75603e0e8f72e734854ac445beb60ed38922bc97b7f7a522795ca4c8c"
+ "sha256:6112e21359ef8f344e7178aa5b72dc6e62b38b0d008e6d3cb212c5b84df72013",
+ "sha256:b0c2d3b226157ae4517d9625decf63591461c66b3a808c2666d538946519d170"
],
- "index": "pypi",
- "version": "==1.2.1"
+ "markers": "python_version < '3.8'",
+ "version": "==3.1.1"
+ },
+ "importlib-resources": {
+ "hashes": [
+ "sha256:7b51f0106c8ec564b1bef3d9c588bc694ce2b92125bbb6278f4f2f5b54ec3592",
+ "sha256:a3d34a8464ce1d5d7c92b0ea4e921e696d86f2aa212e684451cb1482c8d84ed5"
+ ],
+ "markers": "python_version < '3.7'",
+ "version": "==3.3.0"
+ },
+ "iniconfig": {
+ "hashes": [
+ "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3",
+ "sha256:8647b85c03813b8680f4ae9c9db2fd7293f8591ea536a10d73d90f6eb4b10aac",
+ "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"
+ ],
+ "version": "==1.1.1"
},
"jinja2": {
"hashes": [
- "sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250",
- "sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49"
+ "sha256:3f172970d5670703bd3812e8ca6459a9a7e069fa8e51b40195f83c81db191ec4",
+ "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0",
+ "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"
],
- "version": "==2.11.1"
- },
- "langdetect": {
- "hashes": [
- "sha256:91a170d5f0ade380db809b3ba67f08e95fe6c6c8641f96d67a51ff7e98a9bf30"
- ],
- "index": "pypi",
- "version": "==1.0.7"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==2.11.2"
},
"markupsafe": {
"hashes": [
@@ -325,8 +1040,10 @@
"sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235",
"sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5",
"sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42",
+ "sha256:19536834abffb3fa155017053c607cb835b2ecc6a3a2554a88043d991dffb736",
"sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff",
"sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b",
+ "sha256:3d61f15e39611aacd91b7e71d903787da86d9e80896e683c0103fced9add7834",
"sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1",
"sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e",
"sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183",
@@ -336,6 +1053,7 @@
"sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15",
"sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1",
"sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e",
+ "sha256:7952deddf24b85c88dab48f6ec366ac6e39d2761b5280f2f9594911e03fcd064",
"sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b",
"sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905",
"sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735",
@@ -354,152 +1072,84 @@
"sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7",
"sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==1.1.1"
},
- "more-itertools": {
- "hashes": [
- "sha256:5dd8bcf33e5f9513ffa06d5ad33d78f31e1931ac9a18f33d37e77a180d393a7c",
- "sha256:b1ddb932186d8a6ac451e1d95844b382f55e12686d51ca0c68b6f61f2ab7a507"
- ],
- "version": "==8.2.0"
- },
"packaging": {
"hashes": [
- "sha256:170748228214b70b672c581a3dd610ee51f733018650740e98c7df862a583f73",
- "sha256:e665345f9eef0c621aa0bf2f8d78cf6d21904eef16a93f020240b704a57f1334"
+ "sha256:05af3bb85d320377db281cf254ab050e1a7ebcbf5410685a9a407e18a1f81236",
+ "sha256:eb41423378682dadb7166144a4926e443093863024de508ca5c9737d6bc08376"
],
- "version": "==20.1"
- },
- "pdftotext": {
- "hashes": [
- "sha256:d37864049581fb13cdcf7b23d4ea23dac7ca2e9c646e8ecac1a39275ab1cae03"
- ],
- "index": "pypi",
- "version": "==2.1.4"
- },
- "pillow": {
- "hashes": [
- "sha256:0a628977ac2e01ca96aaae247ec2bd38e729631ddf2221b4b715446fd45505be",
- "sha256:4d9ed9a64095e031435af120d3c910148067087541131e82b3e8db302f4c8946",
- "sha256:54ebae163e8412aff0b9df1e88adab65788f5f5b58e625dc5c7f51eaf14a6837",
- "sha256:5bfef0b1cdde9f33881c913af14e43db69815c7e8df429ceda4c70a5e529210f",
- "sha256:5f3546ceb08089cedb9e8ff7e3f6a7042bb5b37c2a95d392fb027c3e53a2da00",
- "sha256:5f7ae9126d16194f114435ebb79cc536b5682002a4fa57fa7bb2cbcde65f2f4d",
- "sha256:62a889aeb0a79e50ecf5af272e9e3c164148f4bd9636cc6bcfa182a52c8b0533",
- "sha256:7406f5a9b2fd966e79e6abdaf700585a4522e98d6559ce37fc52e5c955fade0a",
- "sha256:8453f914f4e5a3d828281a6628cf517832abfa13ff50679a4848926dac7c0358",
- "sha256:87269cc6ce1e3dee11f23fa515e4249ae678dbbe2704598a51cee76c52e19cda",
- "sha256:875358310ed7abd5320f21dd97351d62de4929b0426cdb1eaa904b64ac36b435",
- "sha256:8ac6ce7ff3892e5deaab7abaec763538ffd011f74dc1801d93d3c5fc541feee2",
- "sha256:91b710e3353aea6fc758cdb7136d9bbdcb26b53cefe43e2cba953ac3ee1d3313",
- "sha256:9d2ba4ed13af381233e2d810ff3bab84ef9f18430a9b336ab69eaf3cd24299ff",
- "sha256:a62ec5e13e227399be73303ff301f2865bf68657d15ea50b038d25fc41097317",
- "sha256:ab76e5580b0ed647a8d8d2d2daee170e8e9f8aad225ede314f684e297e3643c2",
- "sha256:bf4003aa538af3f4205c5fac56eacaa67a6dd81e454ffd9e9f055fff9f1bc614",
- "sha256:bf598d2e37cf8edb1a2f26ed3fb255191f5232badea4003c16301cb94ac5bdd0",
- "sha256:c18f70dc27cc5d236f10e7834236aff60aadc71346a5bc1f4f83a4b3abee6386",
- "sha256:c5ed816632204a2fc9486d784d8e0d0ae754347aba99c811458d69fcdfd2a2f9",
- "sha256:dc058b7833184970d1248135b8b0ab702e6daa833be14035179f2acb78ff5636",
- "sha256:ff3797f2f16bf9d17d53257612da84dd0758db33935777149b3334c01ff68865"
- ],
- "index": "pypi",
- "version": "==7.0.0"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==20.7"
},
"pluggy": {
"hashes": [
"sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
"sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
],
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==0.13.1"
},
- "ply": {
- "hashes": [
- "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3",
- "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce"
- ],
- "version": "==3.11"
- },
- "psycopg2": {
- "hashes": [
- "sha256:4212ca404c4445dc5746c0d68db27d2cbfb87b523fe233dc84ecd24062e35677",
- "sha256:47fc642bf6f427805daf52d6e52619fe0637648fe27017062d898f3bf891419d",
- "sha256:72772181d9bad1fa349792a1e7384dde56742c14af2b9986013eb94a240f005b",
- "sha256:8396be6e5ff844282d4d49b81631772f80dabae5658d432202faf101f5283b7c",
- "sha256:893c11064b347b24ecdd277a094413e1954f8a4e8cdaf7ffbe7ca3db87c103f0",
- "sha256:92a07dfd4d7c325dd177548c4134052d4842222833576c8391aab6f74038fc3f",
- "sha256:965c4c93e33e6984d8031f74e51227bd755376a9df6993774fd5b6fb3288b1f4",
- "sha256:9ab75e0b2820880ae24b7136c4d230383e07db014456a476d096591172569c38",
- "sha256:b0845e3bdd4aa18dc2f9b6fb78fbd3d9d371ad167fd6d1b7ad01c0a6cdad4fc6",
- "sha256:dca2d7203f0dfce8ea4b3efd668f8ea65cd2b35112638e488a4c12594015f67b",
- "sha256:ed686e5926929887e2c7ae0a700e32c6129abb798b4ad2b846e933de21508151",
- "sha256:ef6df7e14698e79c59c7ee7cf94cd62e5b869db369ed4b1b8f7b729ea825712a",
- "sha256:f898e5cc0a662a9e12bde6f931263a1bbd350cfb18e1d5336a12927851825bb6"
- ],
- "index": "pypi",
- "version": "==2.8.4"
- },
"py": {
"hashes": [
- "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa",
- "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"
+ "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2",
+ "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"
],
- "version": "==1.8.1"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==1.9.0"
},
"pycodestyle": {
"hashes": [
- "sha256:95a2219d12372f05704562a14ec30bc76b05a5b297b21a5dfe3f6fac3491ae56",
- "sha256:e40a936c9a450ad81df37f549d676d127b1b66000a6c500caa2b085bc0ca976c"
+ "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367",
+ "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"
],
"index": "pypi",
- "version": "==2.5.0"
+ "version": "==2.6.0"
},
"pygments": {
"hashes": [
- "sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b",
- "sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe"
+ "sha256:ccf3acacf3782cbed4a989426012f1c535c9a90d3a7fc3f16d231b9372d2b716",
+ "sha256:f275b6c0909e5dafd2d6269a656aa90fa58ebf4a74f8fcf9053195d226b24a08"
],
- "version": "==2.5.2"
- },
- "pyocr": {
- "hashes": [
- "sha256:fa15adc7e1cf0d345a2990495fe125a947c6e09a60ddba0256a1c14b2e603179"
- ],
- "index": "pypi",
- "version": "==0.7.2"
+ "markers": "python_version >= '3.5'",
+ "version": "==2.7.3"
},
"pyparsing": {
"hashes": [
- "sha256:4c830582a84fb022400b85429791bc551f1f4871c33f23e44f353119e92f969f",
- "sha256:c342dccb5250c08d45fd6f8b4a559613ca603b57498511740e65cd11a2e7dcec"
+ "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
+ "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
],
- "version": "==2.4.6"
+ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==2.4.7"
},
"pytest": {
"hashes": [
- "sha256:0d5fe9189a148acc3c3eb2ac8e1ac0742cb7618c084f3d228baaec0c254b318d",
- "sha256:ff615c761e25eb25df19edddc0b970302d2a9091fbce0e7213298d85fb61fef6"
+ "sha256:4288fed0d9153d9646bfcdf0c0428197dba1ecb27a33bb6e031d002fa88653fe",
+ "sha256:c0a7e94a8cdbc5422a51ccdad8e6f1024795939cc89159a0ae7f0b316ad3823e"
],
"index": "pypi",
- "version": "==5.3.5"
+ "version": "==6.1.2"
},
"pytest-cov": {
"hashes": [
- "sha256:cc6742d8bac45070217169f5f72ceee1e0e55b0221f54bcf24845972d3a47f2b",
- "sha256:cdbdef4f870408ebdbfeb44e63e07eb18bb4619fae852f6e760645fa36172626"
+ "sha256:45ec2d5182f89a81fc3eb29e3d1ed3113b9e9a873bcddb2a71faaab066110191",
+ "sha256:47bd0ce14056fdd79f93e1713f88fad7bdcc583dcd7783da86ef2f085a0bb88e"
],
"index": "pypi",
- "version": "==2.8.1"
+ "version": "==2.10.1"
},
"pytest-django": {
"hashes": [
- "sha256:456fa6854d04ee625d6bbb8b38ca2259e7040a6f93333bfe8bc8159b7e987203",
- "sha256:489b904f695f9fb880ce591cf5a4979880afb467763b1f180c07574554bdfd26"
+ "sha256:10e384e6b8912ded92db64c58be8139d9ae23fb8361e5fc139d8e4f8fc601bc2",
+ "sha256:26f02c16d36fd4c8672390deebe3413678d89f30720c16efb8b2a6bf63b9041f"
],
"index": "pypi",
- "version": "==3.8.0"
+ "version": "==4.1.0"
},
"pytest-env": {
"hashes": [
+ "sha256:33b4030383a021924fe3f3ba5ca4311990d8b1d02ca77389c2be020c4500f96a",
"sha256:7e94956aef7f2764f3c147d216ce066bf6c42948bb9e293169b1b1c880a580c2"
],
"index": "pypi",
@@ -507,26 +1157,28 @@
},
"pytest-forked": {
"hashes": [
- "sha256:1805699ed9c9e60cb7a8179b8d4fa2b8898098e82d229b0825d8095f0f261100",
- "sha256:1ae25dba8ee2e56fb47311c9638f9e58552691da87e82d25b0ce0e4bf52b7d87"
+ "sha256:2d1bfc93ab65a28324eb0a63503bfb500c2da6916efede7a24b43a04970fe63c",
+ "sha256:6aa9ac7e00ad1a539c41bec6d21011332de671e938c7637378ec9710204e37ca",
+ "sha256:dc4147784048e70ef5d437951728825a131b81714b398d5d52f17c7c144d8815"
],
- "version": "==1.1.3"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==1.3.0"
},
"pytest-sugar": {
"hashes": [
- "sha256:26cf8289fe10880cbbc130bd77398c4e6a8b936d8393b116a5c16121d95ab283",
- "sha256:fcd87a74b2bce5386d244b49ad60549bfbc4602527797fac167da147983f58ab"
+ "sha256:67a55a83c7b2717ad607704d3fe9004bb6543b54017ef82f9c6590acc38c1aec",
+ "sha256:b1b2186b0a72aada6859bea2a5764145e3aaa2c1cfbb23c3a19b5f7b697563d3"
],
"index": "pypi",
- "version": "==0.9.2"
+ "version": "==0.9.4"
},
"pytest-xdist": {
"hashes": [
- "sha256:0f46020d3d9619e6d17a65b5b989c1ebbb58fc7b1da8fb126d70f4bac4dfeed1",
- "sha256:7dc0d027d258cd0defc618fb97055fbd1002735ca7a6d17037018cf870e24011"
+ "sha256:7c629016b3bb006b88ac68e2b31551e7becf173c76b977768848e2bbed594d90",
+ "sha256:82d938f1a24186520e2d9d3a64ef7d9ac7ecdf1a0659e095d18e596b8cbd0672"
],
"index": "pypi",
- "version": "==1.31.0"
+ "version": "==2.1.0"
},
"python-dateutil": {
"hashes": [
@@ -536,75 +1188,28 @@
"index": "pypi",
"version": "==2.8.1"
},
- "python-dotenv": {
- "hashes": [
- "sha256:8429f459fc041237d98c9ff32e1938e7e5535b5ff24388876315a098027c3a57",
- "sha256:ca9f3debf2262170d6f46571ce4d6ca1add60bb93b69c3a29dcb3d1a00a65c93"
- ],
- "index": "pypi",
- "version": "==0.11.0"
- },
- "python-gnupg": {
- "hashes": [
- "sha256:3353e59949cd2c15efbf1fca45e347d8a22f4bed0d93e9b89b2657bda19cec05",
- "sha256:c095a41f310ad7a4fd393406660ac9bd6c175ccaa0f072f9c18f33be8130a27a"
- ],
- "index": "pypi",
- "version": "==0.4.5"
- },
- "python-levenshtein": {
- "hashes": [
- "sha256:033a11de5e3d19ea25c9302d11224e1a1898fe5abd23c61c7c360c25195e3eb1"
- ],
- "version": "==0.12.0"
- },
"pytz": {
"hashes": [
- "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d",
- "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be"
+ "sha256:3e6b7dd2d1e0a59084bcee14a17af60c5c562cdc16d828e8eba2e683d3a7e268",
+ "sha256:5c55e189b682d420be27c6995ba6edce0c0a77dd67bfbe2ae6607134d5851ffd"
],
- "index": "pypi",
- "version": "==2019.3"
- },
- "regex": {
- "hashes": [
- "sha256:07b39bf943d3d2fe63d46281d8504f8df0ff3fe4c57e13d1656737950e53e525",
- "sha256:0932941cdfb3afcbc26cc3bcf7c3f3d73d5a9b9c56955d432dbf8bbc147d4c5b",
- "sha256:0e182d2f097ea8549a249040922fa2b92ae28be4be4895933e369a525ba36576",
- "sha256:10671601ee06cf4dc1bc0b4805309040bb34c9af423c12c379c83d7895622bb5",
- "sha256:23e2c2c0ff50f44877f64780b815b8fd2e003cda9ce817a7fd00dea5600c84a0",
- "sha256:26ff99c980f53b3191d8931b199b29d6787c059f2e029b2b0c694343b1708c35",
- "sha256:27429b8d74ba683484a06b260b7bb00f312e7c757792628ea251afdbf1434003",
- "sha256:3e77409b678b21a056415da3a56abfd7c3ad03da71f3051bbcdb68cf44d3c34d",
- "sha256:4e8f02d3d72ca94efc8396f8036c0d3bcc812aefc28ec70f35bb888c74a25161",
- "sha256:4eae742636aec40cf7ab98171ab9400393360b97e8f9da67b1867a9ee0889b26",
- "sha256:6a6ae17bf8f2d82d1e8858a47757ce389b880083c4ff2498dba17c56e6c103b9",
- "sha256:6a6ba91b94427cd49cd27764679024b14a96874e0dc638ae6bdd4b1a3ce97be1",
- "sha256:7bcd322935377abcc79bfe5b63c44abd0b29387f267791d566bbb566edfdd146",
- "sha256:98b8ed7bb2155e2cbb8b76f627b2fd12cf4b22ab6e14873e8641f266e0fb6d8f",
- "sha256:bd25bb7980917e4e70ccccd7e3b5740614f1c408a642c245019cff9d7d1b6149",
- "sha256:d0f424328f9822b0323b3b6f2e4b9c90960b24743d220763c7f07071e0778351",
- "sha256:d58e4606da2a41659c84baeb3cfa2e4c87a74cec89a1e7c56bee4b956f9d7461",
- "sha256:e3cd21cc2840ca67de0bbe4071f79f031c81418deb544ceda93ad75ca1ee9f7b",
- "sha256:e6c02171d62ed6972ca8631f6f34fa3281d51db8b326ee397b9c83093a6b7242",
- "sha256:e7c7661f7276507bce416eaae22040fd91ca471b5b33c13f8ff21137ed6f248c",
- "sha256:ecc6de77df3ef68fee966bb8cb4e067e84d4d1f397d0ef6fce46913663540d77"
- ],
- "version": "==2020.1.8"
+ "version": "==2020.4"
},
"requests": {
"hashes": [
- "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
- "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
+ "sha256:7f1a0b932f4a60a1a65caa4263921bb7d9ee911957e0ae4a23a6dd08185ad5f8",
+ "sha256:e786fa28d8c9154e6a4de5d46a1d921b8749f8b74e28bde23768e5e16eece998"
],
- "version": "==2.22.0"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'",
+ "version": "==2.25.0"
},
"six": {
"hashes": [
- "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
- "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
+ "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
+ "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
],
- "version": "==1.14.0"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==1.15.0"
},
"snowballstemmer": {
"hashes": [
@@ -615,56 +1220,71 @@
},
"sphinx": {
"hashes": [
- "sha256:5024a67f065fe60d9db2005580074d81f22a02dd8f00a5b1ec3d5f4d42bc88d8",
- "sha256:f929b72e0cfe45fa581b8964d54457117863a6a6c9369ecc1a65b8827abd3bf2"
+ "sha256:1e8d592225447104d1172be415bc2972bd1357e3e12fdc76edf2261105db4300",
+ "sha256:d4e59ad4ea55efbb3c05cde3bfc83bfc14f0c95aa95c3d75346fcce186a47960"
],
"index": "pypi",
- "version": "==2.4.1"
+ "version": "==3.3.1"
+ },
+ "sphinx-rtd-theme": {
+ "hashes": [
+ "sha256:22c795ba2832a169ca301cd0a083f7a434e09c538c70beb42782c073651b707d",
+ "sha256:373413d0f82425aaa28fb288009bf0d0964711d347763af2f1b65cafcb028c82"
+ ],
+ "index": "pypi",
+ "version": "==0.5.0"
},
"sphinxcontrib-applehelp": {
"hashes": [
- "sha256:edaa0ab2b2bc74403149cb0209d6775c96de797dfd5b5e2a71981309efab3897",
- "sha256:fb8dee85af95e5c30c91f10e7eb3c8967308518e0f7488a2828ef7bc191d0d5d"
+ "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a",
+ "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"
],
- "version": "==1.0.1"
+ "markers": "python_version >= '3.5'",
+ "version": "==1.0.2"
},
"sphinxcontrib-devhelp": {
"hashes": [
- "sha256:6c64b077937330a9128a4da74586e8c2130262f014689b4b89e2d08ee7294a34",
- "sha256:9512ecb00a2b0821a146736b39f7aeb90759834b07e81e8cc23a9c70bacb9981"
+ "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e",
+ "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"
],
- "version": "==1.0.1"
+ "markers": "python_version >= '3.5'",
+ "version": "==1.0.2"
},
"sphinxcontrib-htmlhelp": {
"hashes": [
- "sha256:4670f99f8951bd78cd4ad2ab962f798f5618b17675c35c5ac3b2132a14ea8422",
- "sha256:d4fd39a65a625c9df86d7fa8a2d9f3cd8299a3a4b15db63b50aac9e161d8eff7"
+ "sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f",
+ "sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b"
],
- "version": "==1.0.2"
+ "markers": "python_version >= '3.5'",
+ "version": "==1.0.3"
},
"sphinxcontrib-jsmath": {
"hashes": [
"sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178",
"sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"
],
+ "markers": "python_version >= '3.5'",
"version": "==1.0.1"
},
"sphinxcontrib-qthelp": {
"hashes": [
- "sha256:513049b93031beb1f57d4daea74068a4feb77aa5630f856fcff2e50de14e9a20",
- "sha256:79465ce11ae5694ff165becda529a600c754f4bc459778778c7017374d4d406f"
+ "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72",
+ "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"
],
- "version": "==1.0.2"
+ "markers": "python_version >= '3.5'",
+ "version": "==1.0.3"
},
"sphinxcontrib-serializinghtml": {
"hashes": [
- "sha256:c0efb33f8052c04fd7a26c0a07f1678e8512e0faec19f4aa8f2473a8b81d5227",
- "sha256:db6615af393650bf1151a6cd39120c29abaf93cc60db8c48eb2dddbfdc3a9768"
+ "sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc",
+ "sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a"
],
- "version": "==1.1.3"
+ "markers": "python_version >= '3.5'",
+ "version": "==1.1.4"
},
"termcolor": {
"hashes": [
+ "sha256:19b1225d03bfb56571484caaa8521d8ec6e2473ae1640c9f48a48dda49417706",
"sha256:1d6d69ce66211143803fbc56652b41d73b4a400a2891d7bf7a1cdf4c02de613b"
],
"version": "==1.1.0"
@@ -678,156 +1298,43 @@
},
"toml": {
"hashes": [
- "sha256:229f81c57791a41d65e399fc06bf0848bab550a9dfd5ed66df18ce5f05e73d5c",
- "sha256:235682dd292d5899d361a811df37e04a8828a5b1da3115886b73cf81ebc9100e"
+ "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
+ "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
],
- "version": "==0.10.0"
+ "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==0.10.2"
},
"tox": {
"hashes": [
- "sha256:5c45d08f1dcc9bc97cdea3e5a69c8f4ad042cc37cbe6cf53e126f4a9005b7d3b",
- "sha256:73e2ade68cd71a2765ee739ecc27c8c92e9b9c09acbad0f2c5307b20214e0d13"
+ "sha256:42ce19ce5dc2f6d6b1fdc5666c476e1f1e2897359b47e0aa3a5b774f335d57c2",
+ "sha256:4321052bfe28f9d85082341ca8e233e3ea901fdd14dab8a5d3fbd810269fbaf6"
],
"index": "pypi",
- "version": "==3.14.4"
- },
- "tzlocal": {
- "hashes": [
- "sha256:11c9f16e0a633b4b60e1eede97d8a46340d042e67b670b290ca526576e039048",
- "sha256:949b9dd5ba4be17190a80c0268167d7e6c92c62b30026cf9764caf3e308e5590"
- ],
- "version": "==2.0.0"
+ "version": "==3.20.1"
},
"urllib3": {
"hashes": [
- "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
- "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
+ "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08",
+ "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473"
],
- "version": "==1.25.8"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'",
+ "version": "==1.26.2"
},
"virtualenv": {
"hashes": [
- "sha256:08f3623597ce73b85d6854fb26608a6f39ee9d055c81178dc6583803797f8994",
- "sha256:de2cbdd5926c48d7b84e0300dea9e8f276f61d186e8e49223d71d91250fbaebd"
+ "sha256:54b05fc737ea9c9ee9f8340f579e5da5b09fb64fd010ab5757eb90268616907c",
+ "sha256:b7a8ec323ee02fb2312f098b6b4c9de99559b462775bc8fe3627a73706603c1b"
],
- "version": "==20.0.4"
+ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
+ "version": "==20.2.2"
},
- "wcwidth": {
+ "zipp": {
"hashes": [
- "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603",
- "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8"
+ "sha256:102c24ef8f171fd729d46599845e95c7ab894a4cf45f5de11a44cc7444fb1108",
+ "sha256:ed5eee1974372595f9e416cc7bbeeb12335201d8081ca8a0743c954d4446e5cb"
],
- "version": "==0.1.8"
- },
- "whitenoise": {
- "hashes": [
- "sha256:0f9137f74bd95fa54329ace88d8dc695fbe895369a632e35f7a136e003e41d73",
- "sha256:62556265ec1011bd87113fb81b7516f52688887b7a010ee899ff1fd18fd22700"
- ],
- "index": "pypi",
- "version": "==5.0.1"
- }
- },
- "develop": {
- "backcall": {
- "hashes": [
- "sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4",
- "sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"
- ],
- "version": "==0.1.0"
- },
- "decorator": {
- "hashes": [
- "sha256:54c38050039232e1db4ad7375cfce6748d7b41c29e95a081c8a6d2c30364a2ce",
- "sha256:5d19b92a3c8f7f101c8dd86afd86b0f061a8ce4540ab8cd401fa2542756bce6d"
- ],
- "version": "==4.4.1"
- },
- "ipython": {
- "hashes": [
- "sha256:d9459e7237e2e5858738ff9c3e26504b79899b58a6d49e574d352493d80684c6",
- "sha256:f6689108b1734501d3b59c84427259fd5ac5141afe2e846cfa8598eb811886c9"
- ],
- "index": "pypi",
- "version": "==7.12.0"
- },
- "ipython-genutils": {
- "hashes": [
- "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8",
- "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"
- ],
- "version": "==0.2.0"
- },
- "jedi": {
- "hashes": [
- "sha256:b4f4052551025c6b0b0b193b29a6ff7bdb74c52450631206c262aef9f7159ad2",
- "sha256:d5c871cb9360b414f981e7072c52c33258d598305280fef91c6cae34739d65d5"
- ],
- "version": "==0.16.0"
- },
- "parso": {
- "hashes": [
- "sha256:56b2105a80e9c4df49de85e125feb6be69f49920e121406f15e7acde6c9dfc57",
- "sha256:951af01f61e6dccd04159042a0706a31ad437864ec6e25d0d7a96a9fbb9b0095"
- ],
- "version": "==0.6.1"
- },
- "pexpect": {
- "hashes": [
- "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937",
- "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"
- ],
- "markers": "sys_platform != 'win32'",
- "version": "==4.8.0"
- },
- "pickleshare": {
- "hashes": [
- "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca",
- "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"
- ],
- "version": "==0.7.5"
- },
- "prompt-toolkit": {
- "hashes": [
- "sha256:a402e9bf468b63314e37460b68ba68243d55b2f8c4d0192f85a019af3945050e",
- "sha256:c93e53af97f630f12f5f62a3274e79527936ed466f038953dfa379d4941f651a"
- ],
- "version": "==3.0.3"
- },
- "ptyprocess": {
- "hashes": [
- "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0",
- "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f"
- ],
- "version": "==0.6.0"
- },
- "pygments": {
- "hashes": [
- "sha256:2a3fe295e54a20164a9df49c75fa58526d3be48e14aceba6d6b1e8ac0bfd6f1b",
- "sha256:98c8aa5a9f778fcd1026a17361ddaf7330d1b7c62ae97c3bb0ae73e0b9b6b0fe"
- ],
- "version": "==2.5.2"
- },
- "six": {
- "hashes": [
- "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a",
- "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c"
- ],
- "version": "==1.14.0"
- },
- "traitlets": {
- "hashes": [
- "sha256:70b4c6a1d9019d7b4f6846832288f86998aa3b9207c6821f3578a6a6a467fe44",
- "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"
- ],
- "version": "==4.3.3"
- },
- "wcwidth": {
- "hashes": [
- "sha256:8fd29383f539be45b20bd4df0dc29c20ba48654a41e661925e612311e9f3c603",
- "sha256:f28b3e8a6483e5d49e7f8949ac1a78314e740333ae305b4ba5defd3e74fb37a8"
- ],
- "version": "==0.1.8"
+ "markers": "python_version >= '3.6'",
+ "version": "==3.4.0"
}
}
}
diff --git a/README-de.md b/README-de.md
deleted file mode 100644
index 386f61027..000000000
--- a/README-de.md
+++ /dev/null
@@ -1,85 +0,0 @@
-[ [en](README.md) | de | [el](README-el.md) ]
-
-
-
-[](https://paperless.readthedocs.org/) [](https://gitter.im/danielquinn/paperless) [](https://travis-ci.org/the-paperless-project/paperless) [](https://coveralls.io/github/the-paperless-project/paperless?branch=master) [](https://github.com/the-paperless-project/paperless/blob/master/THANKS.md)
-
-Indexiere und archiviere alle deine eingescannten Papierdokumente
-
-Ich hasse Papier. Abgesehen von Umweltproblemen, ist es der Albtraum einer technisch-interessierten Person:
-
-* Es gibt keine Suchfunktion
-* Es braucht physischen Platz
-* Sicherungen bedeuten mehr Papier
-
-In den vergangenen Monaten hatte ich mehrmals das Problem, das richtige Dokument nicht zur Hand zu haben. Manchmal warf ich Dokumente weg, die ich noch gebraucht hätte (wer behält schon Wasserrechnungen für zwei Jahre?), andere verlor ich einfach... weil PAPIER. Ich schrieb dies, um mein Leben einfacher zu machen.
-
-
-
-## Wie es funktioniert
-
-Paperless steuert nicht deinen Scanner, es hilft nur damit umzugehen, was der Scanner herausspuckt
-
-1. Kaufe einen Dokumentenscanner, der an einen Ort in deinem Netzwerk schreiben kann. Wenn du Inspirationen brauchst, schau in die [Scannerempfehlungen](https://paperless.readthedocs.io/en/latest/scanners.html).
-2. Stelle "Scanne zu FTP" oder ähnliches ein. Es sollte möglich sein, eingescannte Bilder ohne etwas tun zu müssen an einen Server hochzuladen. Natürlich kannst du auch die einscannte Datei händisch hochladen, wenn der Scanner automatisches Hochladen nicht unterstützt. Paperless ist es egal, wie die Dokumente in seinen lokalen Konsumordner gelangen.
-3. Besitze einen Zielserver, lasse das Paperless-Konsumskript laufen, um die Datei mit OCR zu versehen und sie in einer lokalen Datenbank zu indexieren.
-4. Benutze die Weboberfläche, um die Datenbank zu durchforsten und zu finden, was du suchst.
-5. Lade die PDF-Datei, die du brauchst/möchtest über die Weboberfläche herunter und mach was auch immer du willst damit. Du kannst es auch drucken und versenden, so als wäre es das Original. In den meisten Fällen wird das niemanden interessieren oder bemerken.
-
-Hier das, was du bekommt:
-
-
-
-
-## Dokumentation
-
-Diese ist komplett verfügbar auf [ReadTheDocs](https://paperless.readthedocs.org/).
-
-
-## Anforderungen
-
-Dies alles ist eine wirklich ziemlich einfache, glänzende und benutzerfreundliche Hülle rund um einige sehr mächtige Werkzeuge.
-
-* [ImageMagick](http://imagemagick.org/) wandelt Bilder zwischen Farbe und Graustufen um.
-* [Tesseract](https://github.com/tesseract-ocr) erledigt die Buchstabenerkennung.
-* [Unpaper](https://github.com/unpaper/unpaper) bereinigt und begradigt das eingescannte Bild.
-* [GNU Privacy Guard](https://gnupg.org/) wird als Verschlüsselungsbackend genutzt.
-* [Python 3](https://python.org/) ist die Sprache des Projekts.
- * [Pillow](https://pypi.python.org/pypi/pillowfight/) lädt die Bilddaten als Python-Objekt, um sie mit PyOCR zu verwenden.
- * [PyOCR](https://github.com/jflesch/pyocr) ist ein glatter, programmatischer Wrapper um Tesseract.
- * [Django](https://www.djangoproject.com/) ist das Framework, auf das dieses Projekt aufbaut.
- * [Python-GNUPG](http://pythonhosted.org/python-gnupg/) entschlüsselt die PDFs auf Abruf, um das Herunterladen unverschlüsselter Dateien zu ermöglichen, während die verschlüsselten Dateien auf der Festplatte bleiben.
-
-
-## Status des Projekts
-
-Dieses Projekt wurde um 2015 gestartet und es gibt viele Leute, die es verwenden. Warum auch immer ist es ziemlich beliebt in Deutschland -- vielleicht kann jemand dort drüben mich über das Warum aufklären.
-
-Ich entwickle keine neuen Funktionen mehr für Paperless, weil es genau das tut, was ich brauche und meine Aufmerksamkeit meinem neuesten Projekt [Aletheia](https://github.com/danielquinn/aletheia) gewidmet ist. Ich verlasse jedoch nicht das Projekt. Ich bin glücklich damit, Pull Requests zu begutachten und Fragen im Issue-Bereich zu beantworten. Wenn du ein Entwickler bist und eine neue Funktion willst, reihe sie in den Issues ein und/oder sende einen PR! Ich bin glücklich damit, neue Sachen hinzuzufügen, habe aber einfach nicht die Zeit, sie selbst zu erarbeiten.
-
-
-## Verknüpfte Projekte
-
-Paperless gibt es bereits seit einer Weile und Leute haben damit angefangen, Sachen rund um Paperless zu entwickeln. Wenn du einer dieser Menschen bist, kannst du dein Projekt zu dieser Liste hinzufügen:
-
-* [Paperless App](https://github.com/bauerj/paperless_app): Eine Android/iOS-App für Paperless.
-* [Paperless Desktop](https://github.com/thomasbrueggemann/paperless-desktop): Eine Desktop-Oberfläche für deine Paperless-Installation. Läuft auf Mac, Linux und Windows.
-* [ansible-role-paperless](https://github.com/ovv/ansible-role-paperless): Eine einfache Möglichkeit, Paperless via Ansible laufen zu lassen.
-* [paperless-cli](https://github.com/stgarf/paperless-cli): Ein golang Kommandozeilenprogramm, welches mit Paperless interagiert.
-
-
-## Ähnliche Projekte
-
-Es gibt da draußen auch das Projekt [Mayan EDMS](https://mayan.readthedocs.org/en/latest/), welches überraschenderweise sehr große überschneidende Techniken hat wie Paperless. Mayan EDMS ist *viel* funktionsreicher und kommt ebenso mit einer glatten UI, aber kommt noch mit Python2; basiert jedoch auch auf Django und verwendet ein Konsummodell mit Tesseract und Unpaper. Es kann sein, dass Paperless weniger Ressourcen verbraucht, aber um ehrlich zu sein, hab ich das noch nicht selbst getestet. Eine Sache jedoch ist klar, *Paperless* ist ein **viel** besserer Name.
-
-
-## Wichtiger Hinweis
-
-Dokumentenscanner werden typischerweise verwendet, um sensible Dokumente zu scannen. Dinge wie die Sozialversicherungsnummer, Steueraufzeichnungen, Rechnungen, etc. Während Paperless die Originaldateien über das Konsumskript verschlüsselt, sind die OCR-Texte *nicht* verschlüsselt und demnach in Klartext gespeichert (es muss durchsuchbar sein, also wenn jemand eine Idee hat, wie man das mit verschlüsselten Daten tun kann: Ich bin ganz Ohr). Das bedeutet, dass Paperless niemals auf einem nicht vertrauten Host laufen sollte. Stattdessen empfehle ich, wenn du es verwenden willst, es lokal auf einem Server in deinem Zuhause laufen zu lassen.
-
-
-## Spenden
-
-Wie mit aller Freier Software, liegt die Macht weniger in den Finanzen als mehr in den gemeinsamen Bemühungen. Ich schätze wirklich jeden Pull Request und Bugreport, der von Benutzern von Paperless getätigt wird, also bitte macht damit weiter. Wenn du jedoch nicht einer für Programmieren/Design/Dokumentation bist und mich wirklich finanziell unterstützen willst, sage ich nicht nein dazu ;-)
-
-Das Ding ist, mir geht es finanziell OK, also würde ich dich darum bitten, an den [Hochkommissar der Vereinten Nationen für Flüchtlinge](https://donate.unhcr.org/int-en/general) zu spenden. Diese machen wichtige Arbeit und brauchen das Geld viel dringender als ich.
diff --git a/README-el.md b/README-el.md
deleted file mode 100644
index 4fe307c0b..000000000
--- a/README-el.md
+++ /dev/null
@@ -1,81 +0,0 @@
-[ [en](README.md) | [de](README-de.md) | el ]
-
-
-
-[](https://paperless.readthedocs.org/) [](https://gitter.im/danielquinn/paperless) [](https://travis-ci.org/the-paperless-project/paperless) [](https://coveralls.io/github/the-paperless-project/paperless?branch=master) [](https://github.com/the-paperless-project/paperless/blob/master/THANKS.md)
-
-Ευρετήριο και αρχείο για όλα σας τα σκαναρισμένα έγγραφα
-
-Μισώ το χαρτί. Πέρα από τα περιβαλλοντικά ζητήματα, είναι ο εφιάλτης ενός τεχνικού.
-
-* Δεν υπάρχει η δυνατότητα της αναζήτησης
-* Πιάνουν πολύ χώρο
-* Τα αντίγραφα ασφαλείας σημάινουν περισσότερο χαρτί
-
-Τους τελευταίους μήνες μου έχει τύχει αρκετές φορές να μην μπορώ να βρω το σωστό έγγραφο. Κάποιες φορές ανακύκλωνα το έγγραφο που χρειαζόμουν (ποιος κρατάει τους λογαριασμούς του νερού για 2 χρόνια;;;) και κάποιες φορές απλά το έχανα ... επειδή έτσι είναι τα χαρτιά. Το έκανα αυτό για να κάνω την ζωή μου πιο εύκολη
-
-
-## Πως δουλεύει
-
-Η εφαρμογή Paperless δεν ελέγχει το scanner σας, αλλά σας βοηθάει με τα αποτελέσματα του scanner σας.
-
-1. Αγοράστε ένα scanner με πρόσβαση στο δίκτυο σας. Αν χρειάζεστε έμπνευση, δείτε την σελίδα με τα [προτεινόμενα scanner](https://paperless.readthedocs.io/en/latest/scanners.html).
-2. Κάντε την ρύθμιση "scan to FTP" ή κάτι παρόμοιο. Θα μπορεί να αποθηκεύει τις σκαναρισμένες εικόνες σε έναν server χωρίς να χρειάζεται να κάνετε κάτι. Φυσικά άμα το scanner σας δεν μπορεί να αποθηκεύσει κάπου τις εικόνες σας αυτόματα μπορείτε να το κάνετε χειροκίνητα. Το Paperless δεν ενδιαφέρεται πως καταλήγουν κάπου τα αρχεία.
-3. Να έχετε τον server που τρέχει το OCR script του Paperless να έχει ευρετήριο στην τοπική βάση δεδομένων.
-4. Χρησιμοποιήστε το web frontend για να επιλέξετε βάση δεδομένων και να βρείτε αυτό που θέλετε.
-5. Κατεβάστε το PDF που θέλετε/χρειάζεστε μέσω του web interface και κάντε ότι θέλετε με αυτό. Μπορείτε ακόμη να το εκτυπώσετε και να το στείλετε, σαν να ήταν το αρχικό. Στις περισσότερες περιπτώσεις κανείς δεν θα το προσέξει ή θα νοιαστεί.
-
-Αυτό είναι που θα πάρετε:
-
-
-
-
-## Documentation
-
-Είναι όλα διαθέσιμα εδώ [ReadTheDocs](https://paperless.readthedocs.org/).
-
-
-## Απαιτήσεις
-
-Όλα αυτά είναι πολύ απλά, και φιλικά προς τον χρήστη, μια συλλογή με πολύτιμα εργαλεία.
-
-* [ImageMagick](http://imagemagick.org/) μετατρέπει τις εικόνες σε έγχρωμες και ασπρόμαυρες.
-* [Tesseract](https://github.com/tesseract-ocr) κάνει την αναγνώρηση των χαρακτήρων.
-* [Unpaper](https://github.com/unpaper/unpaper) despeckles and deskews the scanned image.
-* [GNU Privacy Guard](https://gnupg.org/) χρησιμοποιείται για κρυπτογράφηση στο backend.
-* [Python 3](https://python.org/) είναι η γλώσσα του project.
- * [Pillow](https://pypi.python.org/pypi/pillowfight/) Φορτώνει την εικόνα σαν αντικείμενο στην python και μπορεί να χρησιμοποιηθεί με PyOCR
- * [PyOCR](https://github.com/jflesch/pyocr) is a slick programmatic wrapper around tesseract.
- * [Django](https://www.djangoproject.com/) το framework με το οποίο έγινε το project.
- * [Python-GNUPG](http://pythonhosted.org/python-gnupg/) Αποκρυπτογραφεί τα PDF αρχεία στη στιγμή ώστε να κατεβάζετε αποκρυπτογραφημένα αρχεία, αφήνοντας τα κρυπτογραφημένα στον δίσκο.
-
-
-## Σταθερότητα
-
-Αυτό το project υπάρχει από το 2015 και υπάρχουν αρκετοί άνθρωποι που το χρησιμοποιούν, παρόλα αυτά βρίσκεται σε διαρκή ανάπτυξη (απλά δείτε πότε commit έχουν γίνει στο git history) οπότε μην περιμένετε να είναι 100% σταθερό. Μπορείτε να κάνετε backup την βάση δεδομένων sqlite3, τον φάκελο media και το configuration αρχείο σας ώστε να είστε ασφαλείς.
-
-
-## Affiliated Projects
-
-Το Paperless υπάρχει εδώ και κάποιο καιρό και άνθρωποι έχουν αρχίσει να φτιάχνουν πράγματα γύρω από αυτό. Αν είσαι ένας από αυτούς τους ανθρώπους, μπορούμε να βάλουμε το project σου σε αυτήν την λίστα:
-
-* [Paperless App](https://github.com/bauerj/paperless_app): Μια εφαρμογή Android / iOS για Paperless.
-* [Paperless Desktop](https://github.com/thomasbrueggemann/paperless-desktop): Μια desktop εφαρμογή για εγκατάσταση του Paperless. Τρέχει σε Mac, Linux, και Windows.
-* [ansible-role-paperless](https://github.com/ovv/ansible-role-paperless): Ένας εύκολο τρόπος για να τρέχει το Paperless μέσω Ansible.
-
-
-## Παρόμοια Projects
-
-Υπάρχει ένα άλλο ṕroject που λέγεται [Mayan EDMS](https://mayan.readthedocs.org/en/latest/) το οποίο έχει παρόμοια τεχνικά χαρακτηριστικά με το Paperless σε εντυπωσιακό βαθμό. Επίσης βασισμένο στο Django και χρησιμοποιώντας το consumer model με Tesseract και Unpaper, Mayan EDMS έχει *πολλά* περισσότερα χαρακτηριστικά και έρχεται με ένα επιδέξιο UI, αλλά είναι ακόμα σε Python 2. Μπορεί να είναι ότι το Paperless καταναλώνει λιγότερους πόρους, αλλά για να είμαι ειλικρινής, αυτό είναι μια εικασία την οποία δεν έχω επιβεβαιώσει μόνος μου. Ένα πράγμα είναι σίγουρο, το *Paperless* έχει **πολύ** καλύτερο όνομα.
-
-
-## Σημαντική Σημείωση
-
-Τα scanner για αρχεία συνήθως χρησιμοποιούνται για ευαίσθητα αρχεία. Πράγματα όπως το ΑΜΚΑ, φορολογικά αρχεία, τιμολόγια κτλπ. Παρόλο που το Paperless κρυπτογραφεί τα αρχικά αρχεία μέσω του consumption script, το κείμενο OCR *δεν είναι* κρυπτογραφημένο και για αυτό αποθηκεύεται (πρέπει να είναι αναζητήσιμο, οπότε αν κάποιος ξέρει να το κάνει αυτό με κρυπτογραφημένα δεδομένα είμαι όλος αυτιά). Αυτό σημάνει ότι το Paperless δεν πρέπει ποτέ να τρέχει σε μη αξιόπιστο πάροχο. Για αυτό συστήνω αν θέλετε να το τρέξετε να το τρέξετε σε έναν τοπικό server σπίτι σας.
-
-
-## Δωρεές
-
-Όπως με όλα τα δωρεάν λογισμικά, η δύναμη δεν βρίσκεται στα οικονομικά αλλά στην συλλογική προσπάθεια. Αλήθεια εκτιμώ κάθε pull request και bug report που προσφέρεται από τους χρήστες του Paperless, οπότε σας παρακαλώ συνεχίστε. Αν παρόλα αυτά, δεν μπορείτε να γράψετε κώδικα/να κάνέτε design/να γράψετε documentation, και θέλετε να συνεισφέρετε οικονομικά, δεν θα πω όχι ;-)
-
-Το θέμα είναι ότι είμαι οικονομικά εντάξει, οπότε θα σας ζητήσω να δωρίσετε τα χρήματα σας εδώ [United Nations High Commissioner for Refugees](https://donate.unhcr.org/int-en/general). Κάνουν σημαντική δουλειά και χρειάζονται τα χρήματα πολύ περισσότερο από ότι εγώ.
diff --git a/README.md b/README.md
index 35709b6d8..32ff2ab4a 100644
--- a/README.md
+++ b/README.md
@@ -1,88 +1,124 @@
-[ en | [de](README-de.md) | [el](README-el.md) ]
+[](https://travis-ci.org/jonaswinkler/paperless-ng)
+[](https://paperless-ng.readthedocs.io/en/latest/?badge=latest)
+[](https://hub.docker.com/r/jonaswinkler/paperless-ng)
+[](https://coveralls.io/github/jonaswinkler/paperless-ng?branch=master)
-
+# Paperless-ng
-[](https://paperless.readthedocs.org/)
-[](https://gitter.im/danielquinn/paperless)
-[](https://travis-ci.org/the-paperless-project/paperless)
-[](https://coveralls.io/github/the-paperless-project/paperless?branch=master)
-[](https://stackshare.io/the-paperless-project/the-paperless-project)
-[](https://github.com/the-paperless-project/paperless/blob/master/THANKS.md)
+[Paperless](https://github.com/the-paperless-project/paperless) is an application by Daniel Quinn and others that indexes your scanned documents and allows you to easily search for documents and store metadata alongside your documents.
-Index and archive all of your scanned paper documents
+Paperless-ng is a fork of the original project, adding a new interface and many other changes under the hood. For a detailed list of changes, have a look at the changelog in the documentation.
-I hate paper. Environmental issues aside, it's a tech person's nightmare:
+This project is still in development and some things may not work as expected.
-* There's no search feature
-* It takes up physical space
-* Backups mean more paper
+# How it Works
-In the past few months I've been bitten more than a few times by the problem of not having the right document around. Sometimes I recycled a document I needed (who keeps water bills for two years?) and other times I just lost it... because paper. I wrote this to make my life easier.
+Paperless does not control your scanner, it only helps you deal with what your scanner produces.
+1. Buy a document scanner that can write to a place on your network. If you need some inspiration, have a look at the [scanner recommendations](https://paperless-ng.readthedocs.io/en/latest/scanners.html) page. Set it up to "scan to FTP" or something similar. It should be able to push scanned images to a server without you having to do anything. Of course if your scanner doesn't know how to automatically upload the file somewhere, you can always do that manually. Paperless doesn't care how the documents get into its local consumption directory.
-## How it Works
+ - Alternatively, you can use any of the mobile scanning apps out there. We have an app that allows you to share documents with paperless, if you're on Android. See the section on affiliated projects.
-Paperless does not control your scanner, it only helps you deal with what your scanner produces
-
-1. Buy a document scanner that can write to a place on your network. If you need some inspiration, have a look at the [scanner recommendations](https://paperless.readthedocs.io/en/latest/scanners.html) page.
-2. Set it up to "scan to FTP" or something similar. It should be able to push scanned images to a server without you having to do anything. Of course if your scanner doesn't know how to automatically upload the file somewhere, you can always do that manually. Paperless doesn't care how the documents get into its local consumption directory.
-3. Have the target server run the Paperless consumption script to OCR the file and index it into a local database.
-4. Use the web frontend to sift through the database and find what you want.
-5. Download the PDF you need/want via the web interface and do whatever you like with it. You can even print it and send it as if it's the original. In most cases, no one will care or notice.
+2. Wait for paperless to process your files. OCR is expensive, and depending on the power of your machine, this might take a bit of time.
+3. Use the web frontend to sift through the database and find what you want.
+4. Download the PDF you need/want via the web interface and do whatever you like with it. You can even print it and send it as if it's the original. In most cases, no one will care or notice.
Here's what you get:
-
+
+# Features
-## Documentation
+* Performs OCR on your documents, adds selectable text to image only documents and adds tags, correspondents and document types to your documents.
+* Paperless stores your documents plain on disk. Filenames and folders are managed by paperless and can be configured freely.
+* Single page application front end. Should be pretty snappy. Will be mobile friendly in the future.
+ * Includes a dashboard that shows basic statistics and has document upload.
+ * Filtering by tags, correspondents, types, and more.
+ * Customizable views can be saved and displayed on the dashboard.
+* Full text search helps you find what you need.
+ * Auto completion suggests relevant words from your documents.
+ * Results are sorted by relevance to your search query.
+ * Highlighting shows you which parts of the document matched the query.
+* Email processing: Paperless adds documents from your email accounts.
+ * Configure multiple accounts and filters for each account.
+ * When adding documents from mails, paperless can move these mails to a new folder, mark them as read, flag them or delete them.
+* Machine learning powered document matching.
+ * Paperless learns from your documents and will be able to automatically assign tags, correspondents and types to documents once you've stored a few documents in paperless.
+* A task processor that processes documents in parallel and also tells you when something goes wrong. On modern multi core systems, consumption is blazing fast.
+* Code cleanup in many, MANY areas. Some of the code from OG paperless was just overly complicated.
+* More tests, more stability.
-It's all available on [ReadTheDocs](https://paperless.readthedocs.io/).
+If you want to see some screenshots of paperless-ng in action, [some are available in the documentation](https://paperless-ng.readthedocs.io/en/latest/screenshots.html).
+For a complete list of changes from paperless, check out the [changelog](https://paperless-ng.readthedocs.io/en/latest/changelog.html)
-## Requirements
+# Roadmap for 1.0
-This is all really a quite simple, shiny, user-friendly wrapper around some very powerful tools.
+- **Bulk editing**. Add/remove metadata from multiple documents at once.
-* [ImageMagick](http://imagemagick.org/) converts the images between colour and greyscale.
-* [Tesseract](https://github.com/tesseract-ocr) does the character recognition.
-* [Unpaper](https://github.com/unpaper/unpaper) despeckles and deskews the scanned image.
-* [GNU Privacy Guard](https://gnupg.org/) is used as the encryption backend.
-* [Python 3](https://python.org/) is the language of the project.
- * [Pillow](https://pypi.python.org/pypi/pillowfight/) loads the image data as a python object to be used with PyOCR.
- * [PyOCR](https://github.com/jflesch/pyocr) is a slick programmatic wrapper around tesseract.
- * [Django](https://www.djangoproject.com/) is the framework this project is written against.
- * [Python-GNUPG](http://pythonhosted.org/python-gnupg/) decrypts the PDFs on-the-fly to allow you to download unencrypted files, leaving the encrypted ones on-disk.
+- Make the front end nice (except mobile).
+- Test coverage at 90%.
+- Fix whatever bugs I and you find.
+## Roadmap for versions beyond 1.0
-## Project Status
+These are things that I want to add to paperless eventually. They are sorted by priority.
-This project has been around since 2015, and there's lots of people using it. For some reason, it's really popular in Germany -- maybe someone over there can clue me in as to why?
+- **More search.** The search backend is incredibly versatile and customizable. Searching is the most important feature of this project and thus, I want to implement things like:
+ - Group and limit search results by correspondent, show “more from this” links in the results.
+ - Ability to search for “Similar documents” in the search results
+- **Nested tags**. Organize tags in a hierarchical structure. This will combine the benefits of folders and tags in one coherent system.
+- **An interactive consumer** that shows its progress for documents it processes on the web page.
+ - With live updates ans websockets. This already works on a dev branch, but requires a lot of new dependencies, which I'm not particular happy about.
+ - Notifications when a document was added with buttons to open the new document right away.
+- **Arbitrary tag colors**. Allow the selection of any color with a color picker.
+- **More file types**. Possibly allow more file types to be processed by paperless, such as office .odt, .doc, .docx documents.
-I am no longer doing new development on Paperless as it does exactly what I need it to and have since turned my attention to my latest project, [Aletheia](https://github.com/danielquinn/aletheia). However, I'm not abandoning this project. I am happy to field pull requests and answer questions in the issue queue. If you're a developer yourself and want a new feature, float it in the issue queue and/or send me a pull request! I'm happy to add new stuff, but I just don't have the time to do that work myself.
+Apart from that, paperless is pretty much feature complete.
+## On the chopping block.
-## Affiliated Projects
+- **GnuPG encrypion.** [Here's a note about encryption in paperless](https://paperless-ng.readthedocs.io/en/latest/administration.html#managing-encryption). The gist of it is that I don't see which attacks this implementation protects against. It gives a false sense of security to users who don't care about how it works.
+
+# Getting started
+
+The recommended way to deploy paperless is docker-compose. Don't clone the repository, grab the latest release to get started instead. The dockerfiles archive contains just the docker files which will pull the image from docker hub. The source archive contains everything you need to build the docker image yourself (i.e. if you want to run on Raspberry Pi).
+
+Read the [documentation](https://paperless-ng.readthedocs.io/en/latest/setup.html#installation) on how to get started.
+
+Alternatively, you can install the dependencies and setup apache and a database server yourself. The documenation has a step by step guide on how to do it.
+
+# Migrating to paperless-ng
+
+Read the section about [migration](https://paperless-ng.readthedocs.io/en/latest/setup.html#migration-to-paperless-ng) in the documentation. Its also entirely possible to go back to paperless by reverting the database migrations.
+
+# Documentation
+
+The documentation for Paperless-ng is available on [ReadTheDocs](https://paperless-ng.readthedocs.io/).
+
+# Suggestions? Questions? Something not working?
+
+Please open an issue and start a discussion about it!
+
+## Feel like helping out?
+
+There's still lots of things to be done, just have a look at that issue log. If you feel like contributing to the project, please do! Bug fixes and improvements to the front end (I just can't seem to get some of these CSS things right) are always welcome. The documentation has some basic information on how to get started.
+
+If you want to implement something big: Please start a discussion about that in the issues! Maybe I've already had something similar in mind and we can make it happen together. However, keep in mind that the general roadmap is to make the existing features stable and get them tested. See the roadmap above.
+
+# Affiliated Projects
Paperless has been around a while now, and people are starting to build stuff on top of it. If you're one of those people, we can add your project to this list:
-* [Paperless App](https://github.com/bauerj/paperless_app): An Android/iOS app for Paperless.
+* [Paperless App](https://github.com/bauerj/paperless_app): An Android/iOS app for Paperless. Updated to work with paperless-ng.
+* [Paperless Share](https://github.com/qcasey/paperless_share). Share any files from your Android application with paperless. Very simple, but works with all of the mobile scanning apps out there that allow you to share scanned documents.
+
+These projects also exist, but their status and compatibility with paperless-ng is unknown.
+
* [Paperless Desktop](https://github.com/thomasbrueggemann/paperless-desktop): A desktop UI for your Paperless installation. Runs on Mac, Linux, and Windows.
* [ansible-role-paperless](https://github.com/ovv/ansible-role-paperless): An easy way to get Paperless running via Ansible.
* [paperless-cli](https://github.com/stgarf/paperless-cli): A golang command line binary to interact with a Paperless instance.
-## Similar Projects
+# Important Note
-There's another project out there called [Mayan EDMS](https://www.mayan-edms.com/) that has a surprising amount of technical overlap with Paperless. Also based on Django and using a consumer model with Tesseract and Unpaper, Mayan EDMS is *much* more featureful and comes with a slick UI as well, but still in Python 2. It may be that Paperless consumes fewer resources, but to be honest, this is just a guess as I haven't tested this myself. One thing's for certain though, *Paperless* is a **way** better name.
-
-
-## Important Note
-
-Document scanners are typically used to scan sensitive documents. Things like your social insurance number, tax records, invoices, etc. While Paperless encrypts the original files via the consumption script, the OCR'd text is *not* encrypted and is therefore stored in the clear (it needs to be searchable, so if someone has ideas on how to do that on encrypted data, I'm all ears). This means that Paperless should never be run on an untrusted host. Instead, I recommend that if you do want to use it, run it locally on a server in your own home.
-
-
-## Donations
-
-As with all Free software, the power is less in the finances and more in the collective efforts. I really appreciate every pull request and bug report offered up by Paperless' users, so please keep that stuff coming. If however, you're not one for coding/design/documentation, and would like to contribute financially, I won't say no ;-)
-
-The thing is, I'm doing ok for money, so I would instead ask you to donate to the [United Nations High Commissioner for Refugees](https://donate.unhcr.org/int-en/general). They're doing important work and they need the money a lot more than I do.
+Document scanners are typically used to scan sensitive documents. Things like your social insurance number, tax records, invoices, etc. Everything is stored in the clear without encryption by default (it needs to be searchable, so if someone has ideas on how to do that on encrypted data, I'm all ears). This means that Paperless should never be run on an untrusted host. Instead, I recommend that if you do want to use it, run it locally on a server in your own home.
diff --git a/THANKS.md b/THANKS.md
deleted file mode 100644
index e470cc05d..000000000
--- a/THANKS.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# Thanks for using Paperless!
-
-Working on this project has been exhausting, but rewarding at the same time.
-It's just wonderful that so many people are using this thing, and in so many
-crazy ways.
-
-This file is here for everyone to post their own stories about how you use this
-code. It helps me to understand who's using it and why, and maybe to give
-others an idea of how it might be used. It's based on a Twitter exchange
-between [John Glanville](https://twitter.com/hexapodium) and
-[Julia Evans](https://github.com/jvns) and later better defined [here](https://github.com/paulmolluzzo/thanks-md).
-
-To contribute, simply issue a pull request that appends to this file something
-like this:
-
-```
-### Your Name
-Some friendly message
-```
diff --git a/ci/deploy-docker b/ci/deploy-docker
deleted file mode 100755
index f8de0666a..000000000
--- a/ci/deploy-docker
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/bash
-
-if [ "${DOCKER_USERNAME}" == "" -o "${DOCKER_PASSWORD}" == "" ]
-then
- exit 0
-fi
-
-docker login --username=${DOCKER_USERNAME} --password=${DOCKER_PASSWORD}
-if [ "${TRAVIS_TAG}" != "" ]
-then
- docker tag the-paperless-project/paperless the-paperless-project/paperless:${TRAVIS_TAG}
- docker push the-paperless-project/paperless:${TRAVIS_TAG}
-else
- docker push the-paperless-project/paperless
-fi
diff --git a/docker-compose.env.example b/docker-compose.env.example
deleted file mode 100644
index 7117c7dad..000000000
--- a/docker-compose.env.example
+++ /dev/null
@@ -1,28 +0,0 @@
-# Environment variables to set for Paperless
-# Commented out variables will be replaced with a default within Paperless.
-#
-# In addition to what you see here, you can also define any values you find in
-# paperless.conf.example here. Values like:
-#
-# * PAPERLESS_PASSPHRASE
-# * PAPERLESS_CONSUMPTION_DIR
-# * PAPERLESS_CONSUME_MAIL_HOST
-#
-# ...are all explained in that file but can be defined here, since the Docker
-# installation doesn't make use of paperless.conf.
-
-# Use this variable to set a timezone for the Paperless Docker containers. If not specified, defaults to UTC.
-# TZ=America/Los_Angeles
-
-# Additional languages to install for text recognition. Note that this is
-# different from PAPERLESS_OCR_LANGUAGE (default=eng), which defines the
-# default language used when guessing the language from the OCR output.
-# PAPERLESS_OCR_LANGUAGES=deu ita
-
-# Set Paperless to use SSL for the web interface.
-# Enabling this will require ssl.key and ssl.cert files in paperless' data directory.
-# PAPERLESS_USE_SSL=false
-
-# You can change the default user and group id to a custom one
-# USERMAP_UID=1000
-# USERMAP_GID=1000
diff --git a/docker-compose.yml.example b/docker-compose.yml.example
deleted file mode 100644
index f8c920f95..000000000
--- a/docker-compose.yml.example
+++ /dev/null
@@ -1,56 +0,0 @@
-version: '2.1'
-
-services:
- webserver:
- build: ./
- # uncomment the following line to start automatically on system boot
- # restart: always
- ports:
- # You can adapt the port you want Paperless to listen on by
- # modifying the part before the `:`.
- - "8000:8000"
- healthcheck:
- test: ["CMD", "curl" , "-f", "http://localhost:8000"]
- interval: 30s
- timeout: 10s
- retries: 5
- volumes:
- - data:/usr/src/paperless/data
- - media:/usr/src/paperless/media
- # You have to adapt the local path you want the consumption
- # directory to mount to by modifying the part before the ':'.
- - ./consume:/consume
- env_file: docker-compose.env
- # The reason the line is here is so that the webserver that doesn't do
- # any text recognition and doesn't have to install unnecessary
- # languages the user might have set in the env-file by overwriting the
- # value with nothing.
- environment:
- - PAPERLESS_OCR_LANGUAGES=
- command: ["gunicorn", "-b", "0.0.0.0:8000"]
-
- consumer:
- build: ./
- # uncomment the following line to start automatically on system boot
- # restart: always
- depends_on:
- webserver:
- condition: service_healthy
- volumes:
- - data:/usr/src/paperless/data
- - media:/usr/src/paperless/media
- # This should be set to the same value as the consume directory
- # in the webserver service above.
- - ./consume:/consume
- # Likewise, you can add a local path to mount a directory for
- # exporting. This is not strictly needed for paperless to
- # function, only if you're exporting your files: uncomment
- # it and fill in a local path if you know you're going to
- # want to export your documents.
- # - /path/to/another/arbitrary/place:/export
- env_file: docker-compose.env
- command: ["document_consumer"]
-
-volumes:
- data:
- media:
diff --git a/docker/docker-compose.env b/docker/docker-compose.env
new file mode 100644
index 000000000..4271bce6e
--- /dev/null
+++ b/docker/docker-compose.env
@@ -0,0 +1,34 @@
+# The UID and GID of the user used to run paperless in the container. Set this
+# to your UID and GID on the host so that you have write access to the
+# consumption directory.
+#USERMAP_UID=1000
+#USERMAP_GID=1000
+
+# Additional languages to install for text recognition, separated by a
+# whitespace. Note that this is
+# different from PAPERLESS_OCR_LANGUAGE (default=eng), which defines the
+# default language used when guessing the language from the OCR output.
+# The container installs English, German, Italian, Spanish and French by
+# default.
+# See https://packages.debian.org/search?keywords=tesseract-ocr-&searchon=names&suite=buster
+# for available languages.
+#PAPERLESS_OCR_LANGUAGES=tur ces
+
+###############################################################################
+# Paperless-specific settings #
+###############################################################################
+
+# All settings defined in the paperless.conf.example can be used here. The
+# Docker setup does not use the configuration file.
+# A few commonly adjusted settings are provided below.
+
+# Adjust this key if you plan to make paperless available publicly. It should
+# be a very long sequence of random characters. You don't need to remember it.
+#PAPERLESS_SECRET_KEY=change-me
+
+# Use this variable to set a timezone for the Paperless Docker containers. If not specified, defaults to UTC.
+#PAPERLESS_TIME_ZONE=America/Los_Angeles
+
+# The default language to use for OCR. Set this to the language most of your
+# documents are written in.
+#PAPERLESS_OCR_LANGUAGE=eng
diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh
new file mode 100644
index 000000000..13a0ba035
--- /dev/null
+++ b/docker/docker-entrypoint.sh
@@ -0,0 +1,134 @@
+#!/bin/bash
+
+set -e
+
+# Source: https://github.com/sameersbn/docker-gitlab/
+map_uidgid() {
+ USERMAP_ORIG_UID=$(id -u paperless)
+ USERMAP_ORIG_GID=$(id -g paperless)
+ USERMAP_NEW_UID=${USERMAP_UID:-$USERMAP_ORIG_UID}
+ USERMAP_NEW_GID=${USERMAP_GID:-${USERMAP_ORIG_GID:-$USERMAP_NEW_UID}}
+ if [[ ${USERMAP_NEW_UID} != "${USERMAP_ORIG_UID}" || ${USERMAP_NEW_GID} != "${USERMAP_ORIG_GID}" ]]; then
+ echo "Mapping UID and GID for paperless:paperless to $USERMAP_NEW_UID:$USERMAP_NEW_GID"
+ usermod -u "${USERMAP_NEW_UID}" paperless
+ groupmod -o -g "${USERMAP_NEW_GID}" paperless
+ fi
+}
+
+
+wait_for_postgres() {
+ attempt_num=1
+ max_attempts=5
+
+ echo "Waiting for PostgreSQL to start..."
+
+ host="${PAPERLESS_DBHOST}"
+ port="${PAPERLESS_DBPORT}"
+
+ if [[ -z $port ]] ;
+ then
+ port="5432"
+ fi
+
+ while !/usr/src/paperless/data/migration_lock
+
+}
+
+initialize() {
+ map_uidgid
+
+ for dir in export data data/index media media/documents media/documents/originals media/documents/thumbnails; do
+ if [[ ! -d "../$dir" ]]
+ then
+ echo "creating directory ../$dir"
+ mkdir ../$dir
+ fi
+ done
+
+ chown -R paperless:paperless ../
+
+ migrations
+
+}
+
+install_languages() {
+ local langs="$1"
+ read -ra langs <<<"$langs"
+
+ # Check that it is not empty
+ if [ ${#langs[@]} -eq 0 ]; then
+ return
+ fi
+ apt-get update
+
+ for lang in "${langs[@]}"; do
+ pkg="tesseract-ocr-$lang"
+ # English is installed by default
+ #if [[ "$lang" == "eng" ]]; then
+ # continue
+ #fi
+
+ if dpkg -s $pkg &> /dev/null; then
+ echo "package $pkg already installed!"
+ continue
+ fi
+
+ if ! apt-cache show $pkg &> /dev/null; then
+ echo "package $pkg not found! :("
+ continue
+ fi
+
+ echo "Installing package $pkg..."
+ if ! apt-get -y install "$pkg" &> /dev/null; then
+ echo "Could not install $pkg"
+ exit 1
+ fi
+ done
+}
+
+# Install additional languages if specified
+if [[ ! -z "$PAPERLESS_OCR_LANGUAGES" ]]; then
+ install_languages "$PAPERLESS_OCR_LANGUAGES"
+fi
+
+initialize
+
+if [[ "$1" != "/"* ]]; then
+ exec sudo -HEu paperless python3 manage.py "$@"
+else
+ exec "$@"
+fi
+
diff --git a/scripts/gunicorn.conf b/docker/gunicorn.conf.py
similarity index 100%
rename from scripts/gunicorn.conf
rename to docker/gunicorn.conf.py
diff --git a/docker/hub/docker-compose.postgres.yml b/docker/hub/docker-compose.postgres.yml
new file mode 100644
index 000000000..d33e4c38d
--- /dev/null
+++ b/docker/hub/docker-compose.postgres.yml
@@ -0,0 +1,44 @@
+version: "3.4"
+services:
+ broker:
+ image: redis:6.0
+ restart: always
+
+ db:
+ image: postgres:13
+ restart: always
+ volumes:
+ - pgdata:/var/lib/postgresql/data
+ environment:
+ POSTGRES_DB: paperless
+ POSTGRES_USER: paperless
+ POSTGRES_PASSWORD: paperless
+
+ webserver:
+ image: jonaswinkler/paperless-ng:0.9.8
+ restart: always
+ depends_on:
+ - db
+ - broker
+ ports:
+ - 8000:8000
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:8000"]
+ interval: 30s
+ timeout: 10s
+ retries: 5
+ volumes:
+ - data:/usr/src/paperless/data
+ - media:/usr/src/paperless/media
+ - ./export:/usr/src/paperless/export
+ - ./consume:/usr/src/paperless/consume
+ env_file: docker-compose.env
+ environment:
+ PAPERLESS_REDIS: redis://broker:6379
+ PAPERLESS_DBHOST: db
+
+
+volumes:
+ data:
+ media:
+ pgdata:
diff --git a/docker/hub/docker-compose.sqlite.yml b/docker/hub/docker-compose.sqlite.yml
new file mode 100644
index 000000000..c130dfef6
--- /dev/null
+++ b/docker/hub/docker-compose.sqlite.yml
@@ -0,0 +1,31 @@
+version: "3.4"
+services:
+ broker:
+ image: redis:6.0
+ restart: always
+
+ webserver:
+ image: jonaswinkler/paperless-ng:0.9.8
+ restart: always
+ depends_on:
+ - broker
+ ports:
+ - 8000:8000
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:8000"]
+ interval: 30s
+ timeout: 10s
+ retries: 5
+ volumes:
+ - data:/usr/src/paperless/data
+ - media:/usr/src/paperless/media
+ - ./export:/usr/src/paperless/export
+ - ./consume:/usr/src/paperless/consume
+ env_file: docker-compose.env
+ environment:
+ PAPERLESS_REDIS: redis://broker:6379
+
+
+volumes:
+ data:
+ media:
diff --git a/docker/imagemagick-policy.xml b/docker/imagemagick-policy.xml
new file mode 100644
index 000000000..095355706
--- /dev/null
+++ b/docker/imagemagick-policy.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docker/local/Dockerfile b/docker/local/Dockerfile
new file mode 100644
index 000000000..461b9e4fc
--- /dev/null
+++ b/docker/local/Dockerfile
@@ -0,0 +1,67 @@
+FROM python:3.7-slim
+
+WORKDIR /usr/src/paperless/
+
+COPY requirements.txt ./
+
+#Dependencies
+RUN apt-get update \
+ && apt-get -y --no-install-recommends install \
+ build-essential \
+ curl \
+ ghostscript \
+ gnupg \
+ icc-profiles-free \
+ imagemagick \
+ libatlas-base-dev \
+ liblept5 \
+ libmagic-dev \
+ libpoppler-cpp-dev \
+ libpq-dev \
+ libqpdf-dev \
+ libxml2 \
+ optipng \
+ pngquant \
+ qpdf \
+ sudo \
+ tesseract-ocr \
+ tesseract-ocr-eng \
+ tesseract-ocr-deu \
+ tesseract-ocr-fra \
+ tesseract-ocr-ita \
+ tesseract-ocr-spa \
+ tzdata \
+ unpaper \
+ zlib1g \
+ && pip3 install --upgrade supervisor setuptools \
+ && pip install --no-cache-dir -r requirements.txt \
+ && apt-get -y purge build-essential libqpdf-dev \
+ && apt-get -y autoremove --purge \
+ && rm -rf /var/lib/apt/lists/* \
+ && mkdir /var/log/supervisord /var/run/supervisord
+
+# copy scripts
+# this fixes issues with imagemagick and PDF
+COPY docker/imagemagick-policy.xml /etc/ImageMagick-6/policy.xml
+COPY docker/gunicorn.conf.py ./
+COPY docker/supervisord.conf /etc/supervisord.conf
+COPY docker/docker-entrypoint.sh /sbin/docker-entrypoint.sh
+
+# copy app
+COPY src/ ./src/
+
+# add users, setup scripts
+RUN addgroup --gid 1000 paperless \
+ && useradd --uid 1000 --gid paperless --home-dir /usr/src/paperless paperless \
+ && chown -R paperless:paperless . \
+ && chmod 755 /sbin/docker-entrypoint.sh
+
+WORKDIR /usr/src/paperless/src/
+
+RUN sudo -HEu paperless python3 manage.py collectstatic --clear --no-input
+
+VOLUME ["/usr/src/paperless/data", "/usr/src/paperless/media", "/usr/src/paperless/consume", "/usr/src/paperless/export"]
+ENTRYPOINT ["/sbin/docker-entrypoint.sh"]
+CMD ["/usr/local/bin/supervisord", "-c", "/etc/supervisord.conf"]
+
+LABEL maintainer="Jonas Winkler "
diff --git a/docker/local/docker-compose.postgres.yml b/docker/local/docker-compose.postgres.yml
new file mode 100644
index 000000000..59ae28939
--- /dev/null
+++ b/docker/local/docker-compose.postgres.yml
@@ -0,0 +1,44 @@
+version: "3.4"
+services:
+ broker:
+ image: redis:6.0
+ restart: always
+
+ db:
+ image: postgres:13
+ restart: always
+ volumes:
+ - pgdata:/var/lib/postgresql/data
+ environment:
+ POSTGRES_DB: paperless
+ POSTGRES_USER: paperless
+ POSTGRES_PASSWORD: paperless
+
+ webserver:
+ build: .
+ restart: always
+ depends_on:
+ - db
+ - broker
+ ports:
+ - 8000:8000
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:8000"]
+ interval: 30s
+ timeout: 10s
+ retries: 5
+ volumes:
+ - data:/usr/src/paperless/data
+ - media:/usr/src/paperless/media
+ - ./export:/usr/src/paperless/export
+ - ./consume:/usr/src/paperless/consume
+ env_file: docker-compose.env
+ environment:
+ PAPERLESS_REDIS: redis://broker:6379
+ PAPERLESS_DBHOST: db
+
+
+volumes:
+ data:
+ media:
+ pgdata:
diff --git a/docker/local/docker-compose.sqlite.yml b/docker/local/docker-compose.sqlite.yml
new file mode 100644
index 000000000..2d21fa3f1
--- /dev/null
+++ b/docker/local/docker-compose.sqlite.yml
@@ -0,0 +1,31 @@
+version: "3.4"
+services:
+ broker:
+ image: redis:6.0
+ restart: always
+
+ webserver:
+ build: .
+ restart: always
+ depends_on:
+ - broker
+ ports:
+ - 8000:8000
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:8000"]
+ interval: 30s
+ timeout: 10s
+ retries: 5
+ volumes:
+ - data:/usr/src/paperless/data
+ - media:/usr/src/paperless/media
+ - ./export:/usr/src/paperless/export
+ - ./consume:/usr/src/paperless/consume
+ env_file: docker-compose.env
+ environment:
+ PAPERLESS_REDIS: redis://broker:6379
+
+
+volumes:
+ data:
+ media:
diff --git a/docker/supervisord.conf b/docker/supervisord.conf
new file mode 100644
index 000000000..ebe0f005d
--- /dev/null
+++ b/docker/supervisord.conf
@@ -0,0 +1,35 @@
+[supervisord]
+nodaemon=true ; start in foreground if true; default false
+logfile=/var/log/supervisord/supervisord.log ; main log file; default $CWD/supervisord.log
+pidfile=/var/run/supervisord/supervisord.pid ; supervisord pidfile; default supervisord.pid
+logfile_maxbytes=50MB ; max main logfile bytes b4 rotation; default 50MB
+logfile_backups=10 ; # of main logfile backups; 0 means none, default 10
+loglevel=info ; log level; default info; others: debug,warn,trace
+user=root
+
+[program:gunicorn]
+command=gunicorn -c /usr/src/paperless/gunicorn.conf.py -b 0.0.0.0:8000 paperless.wsgi
+user=paperless
+
+stdout_logfile=/dev/stdout
+stdout_logfile_maxbytes=0
+stderr_logfile=/dev/stderr
+stderr_logfile_maxbytes=0
+
+[program:consumer]
+command=python3 manage.py document_consumer
+user=paperless
+
+stdout_logfile=/dev/stdout
+stdout_logfile_maxbytes=0
+stderr_logfile=/dev/stderr
+stderr_logfile_maxbytes=0
+
+[program:scheduler]
+command=python3 manage.py qcluster
+user=paperless
+
+stdout_logfile=/dev/stdout
+stdout_logfile_maxbytes=0
+stderr_logfile=/dev/stderr
+stderr_logfile_maxbytes=0
diff --git a/docs/_static/Screenshot_first_logged.png b/docs/_static/Screenshot_first_logged.png
deleted file mode 100644
index 9b9c6072b..000000000
Binary files a/docs/_static/Screenshot_first_logged.png and /dev/null differ
diff --git a/docs/_static/Screenshot_first_run_login.png b/docs/_static/Screenshot_first_run_login.png
deleted file mode 100644
index d704f1682..000000000
Binary files a/docs/_static/Screenshot_first_run_login.png and /dev/null differ
diff --git a/docs/_static/Screenshot_upload_and_scanned.png b/docs/_static/Screenshot_upload_and_scanned.png
deleted file mode 100644
index 7b433b2ca..000000000
Binary files a/docs/_static/Screenshot_upload_and_scanned.png and /dev/null differ
diff --git a/docs/_static/lxc-install.svg b/docs/_static/lxc-install.svg
deleted file mode 100644
index 7ffe1bbdf..000000000
--- a/docs/_static/lxc-install.svg
+++ /dev/null
@@ -1,44 +0,0 @@
-
-
-
-
-
-
-
-
-
- Need to get 1818 kB of archives. Collecting ipython-genutils==0.2.0 (from -r requirements.txt (line 32)) 24% [Waiting for headers] Setting up libfile-fcntllock-perl (0.22-3build2) ... Collecting sphinxcontrib-websupport==1.1.0 (from -r requirements.txt (line 71)) Get:45 http://archive.ubuntu.com/ubuntu bionic/main amd64 libxcb-shm0 amd64 1.13-1 [5572 B] 19% |██████▎ | 614kB 5.2MB/s eta 0:00:01 99% |████████████████████████████████| 3.1MB 6.0MB/s eta 0:00:01 bms@pingu : ~ $ ex -extensions-2.1.3 django-filter-2.0.0 djangorestframework-3.9.0 docopt-0.6.2 docutils-0.14 execnet-1.5.0 factory-boy-2.11.1 faker-0. Unpacking liblqr-1-0:amd64 (0.4.2-2.1) ... Selecting previously unselected package python3-xdg. 52% |████████████████▉ | 266kB 6.0MB/s eta 0:00:01 62% |████████████████████ | 5.1MB 5.8MB/s eta 0:00:01 100% |████████████████████████████████| 215kB 2.6MB/s 50% [Waiting for headers] 3755 kB/s 8s Downloading https://files.pythonhosted.org/packages/1b/92/a45a140b4024988f65eb76020101fa6da968b43716121595452b8bafb506/pytest_xdis binutils binutils-common binutils-x86-64-linux-gnu build-essential cpp cpp-7 dh-python dpkg-dev fakeroot g++ g++-7 gcc gcc-7 Not uninstalling urllib3 at /usr/lib/python3/dist-packages, outside environment /usr python-crypto-doc gnome-keyring libkf5wallet-bin gir1.2-gnomekeyring-1.0 python-secretstorage-doc python-setuptools-doc Get:104 http://archive.ubuntu.com/ubuntu bionic/universe amd64 liblept5 amd64 1.75.3-3 [929 kB] 76% |████████████████████████▌ | 1.5MB 6.0MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/fi.js' bms@pingu : ~ $ # Lanuch the new machine Selecting previously unselected package libpoppler-cpp0v5:amd64. Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/he.js' Setting up tesseract-ocr (4.00~git2288-10f4998a-2) ... Adding system user `ftp' (UID 112) ... Downloading https://files.pythonhosted.org/packages/dd/9d/fb75af584b850a902c9ce5ec96ea5c623978113b8a240ab414e1a435df93/pytest_fork 62% |███████████████████▉ | 522kB 5.9MB/s eta 0:00:01 Get:15 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 imagemagick-6-common all 8:6.9.7.4+dfsg-16ubuntu6.4 [60.9 kB] Get:19 http://archive.ubuntu.com/ubuntu bionic/main amd64 poppler-data all 0.4.8-2 [1479 kB] Selecting previously unselected package python-pip-whl. Preparing to unpack .../048-libgsm1_1.0.13-4build1_amd64.deb ... 59% [43 libpython3.6-dev 12.1 MB/44.8 MB 27%] 4081 kB/s 9s Get:10 http://archive.ubuntu.com/ubuntu bionic/main amd64 libmpc3 amd64 1.1.0-1 [40.8 kB] 49% [10 Sources store 0 B] [18 Packages 2685 B/8570 kB 0%] python3-pip python3-secretstorage python3-setuptools python3-venv python3-wheel python3-xdg python3.6-dev python3.6-venv Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/vi.js' Reading package lists... 88% Receiving objects: 14% (780/5570) Setting up libpoppler-cpp-dev:amd64 (0.62.0-2ubuntu2.5) ... 79% |█████████████████████████▍ | 5.6MB 6.1MB/s eta 0:00:01 Setting up gcc-7-base:amd64 (7.3.0-27ubuntu1~18.04) ... 90% [18 Packages store 0 B] [21 Translation-en 2687 B/108 kB 2%] libcups2 libcupsfilters1 libcupsimage2 libdatrie1 libdjvulibre-text libdjvulibre21 libdrm-amdgpu1 libdrm-intel1 libdrm-nouveau2 40% |████████████▉ | 2.9MB 5.9MB/s eta 0:00:01 Extracting templates from packages: 85% Stored in directory: /root/.cache/pip/wheels/7c/06/54/bc84598ba1daf8f970247f550b175aaaee85f68b4b0c5ab2c6 41% [Waiting for headers] 3755 kB/s 10s Applying sessions.0001_initial... OK Selecting previously unselected package libxrender1:amd64. Selecting previously unselected package vdpau-driver-all:amd64. Get:3 http://archive.ubuntu.com/ubuntu bionic/main amd64 libfontconfig1 amd64 2.12.6-0ubuntu2 [137 kB] Get:83 http://archive.ubuntu.com/ubuntu bionic-updates/universe amd64 libavcodec57 amd64 7:3.4.4-0ubuntu0.18.04.1 [4592 kB] Unpacking libfontconfig1:amd64 (2.12.6-0ubuntu2) ... Unpacking libxcb-shm0:amd64 (1.13-1) ... 23% [11 Packages store 0 B] [10 Sources 1142 kB/9051 kB 13%] [Waiting for headers] 91% |█████████████████████████████▎ | 7.4MB 5.9MB/s eta 0:00:01 Preparing to unpack .../095-libdrm-amdgpu1_2.4.91-2_amd64.deb ... 52% |████████████████▉ | 112kB 6.5MB/s eta 0:00:01 Setting up python3-crypto (2.6.1-8ubuntu2) ... Adding new group `paperless' (1000) ... 50% [43 libpython3.6-dev 2540 kB/44.8 MB 6%] 4081 kB/s 11s Selecting previously unselected package libjpeg8:amd64. 76% |████████████████████████▍ | 5.4MB 6.0MB/s eta 0:00:01 binutils-doc cpp-doc gcc-7-locales debian-keyring g++-multilib g++-7-multilib gcc-7-doc libstdc++6-7-dbg gcc-multilib autoconf 61% |███████████████████▊ | 614kB 5.9MB/s eta 0:00:01 Stored in directory: /root/.cache/pip/wheels/15/ae/df/a67bf1ed84e9bf230187d36d8dcfd30072bea0236cb059ed91 passwd: password updated successfully Get:26 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 Packages [489 kB] 81% |██████████████████████████▏ | 92kB 5.8MB/s eta 0:00:01 pygments, traitlets, wcwidth, prompt-toolkit, ipython, langdetect, more-itertools, pyparsing, packaging, pdftotext, pillow, pluggy, Unpacking libltdl7:amd64 (2.4.6-2) ... Selecting previously unselected package libdpkg-perl. 10% |███▍ | 204kB 6.0MB/s eta 0:00:01 Selecting previously unselected package libpoppler73:amd64. Get:6 http://security.ubuntu.com/ubuntu bionic-security/main Sources [70.2 kB] Applying documents.0017_auto_20170512_0507... OK Preparing to unpack .../16-liblsan0_8.2.0-1ubuntu2~18.04_amd64.deb ... Enabling module access_compat. Get:38 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libva-drm2 amd64 2.1.0-3 [6880 B] Password: Downloading https://files.pythonhosted.org/packages/21/35/60094dbadd9de2035873390b1cac25e01da605844eba6a07a53a82fa4adc/pdftotext-2 76% [Waiting for headers] 3755 kB/s 4s Collecting faker==0.9.2 (from -r requirements.txt (line 24)) Unpacking libsoxr0:amd64 (0.1.2-3) ... Preparing to unpack .../020-libavahi-common-data_0.7-3.1ubuntu1.1_amd64.deb ... 33% [50 libmp3lame0 4061 B/136 kB 3%] Setting up i965-va-driver:amd64 (2.1.0-0ubuntu1) ... Get:34 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 python3-distutils all 3.6.7-1~18.04 [141 kB] Setting up libdatrie1:amd64 (0.2.10-7) ... Get:11 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 liblcms2-2 amd64 2.9-1ubuntu0.1 [139 kB] 83% |██████████████████████████▉ | 6.8MB 6.0MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/it.js' Preparing to unpack .../09-libapache2-mod-wsgi-py3_4.5.17-1_amd64.deb ... 52% |████████████████▊ | 522kB 5.9MB/s eta 0:00:01 Selecting previously unselected package fontconfig. Get:17 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 liblsan0 amd64 8.2.0-1ubuntu2~18.04 [132 kB] Collecting urllib3==1.24.1 (from -r requirements.txt (line 78)) Downloading https://files.pythonhosted.org/packages/3a/9a/9d878f8d885706e2530402de6417141129a943802c084238914fa6798d97/atomicwrite Selecting previously unselected package libexpat1-dev:amd64. bms@FortBoyard:~$ exi Setting up libgs9-common (9.26~dfsg+0-0ubuntu0.18.04.3) ... 45% |██████████████▍ | 890kB 5.9MB/s eta 0:00:01 bms@pingu : ~ $ e Collecting tox==3.5.3 (from -r requirements.txt (line 75)) Preparing to unpack .../19-libcilkrts5_7.3.0-27ubuntu1~18.04_amd64.deb ... Setting up libatomic1:amd64 (8.2.0-1ubuntu2~18.04) ... 20% |██████▋ | 1.7MB 5.6MB/s eta 0:00:02 Preparing to unpack .../016-libmagickwand-6.q16-3_8%3a6.9.7.4+dfsg-16ubuntu6.4_amd64.deb ... 10% [Waiting for headers] 99% |████████████████████████████████| 839kB 6.6MB/s eta 0:00:01 Selecting previously unselected package libcilkrts5:amd64. Unpacking libcups2:amd64 (2.2.7-1ubuntu2.3) ... fonts-noto ghostscript-x i965-va-driver-shaders imagemagick-doc autotrace cups-bsd | lpr | lprng enscript ffmpeg gimp gnuplot 18% |██████ | 501kB 5.8MB/s eta 0:00:01 Get:21 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libavahi-common-data amd64 0.7-3.1ubuntu1.1 [22.1 kB] 52% |████████████████▊ | 4.2MB 5.9MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/css/dashboard.css' Setting up libnspr4:amd64 (2:4.18-1ubuntu1) ... Running setup.py bdist_wheel for pdftotext ... \ 39% [74 libvpx5 2613 B/798 kB 0%] 3755 kB/s 11s Preparing to unpack .../32-python3-lib2to3_3.6.7-1~18.04_all.deb ... 52% [43 libpython3.6-dev 4765 kB/44.8 MB 11%] 4081 kB/s 11s Selecting previously unselected package libxfixes3:amd64. 48% [10 Sources 8678 kB/9051 kB 96%] Unpacking libmagickcore-6.q16-3-extra:amd64 (8:6.9.7.4+dfsg-16ubuntu6.4) ... 34% [Waiting for headers] Enabling module mime. Creating home directory `/home/paperless' ... 41% |█████████████▏ | 1.1MB 5.9MB/s eta 0:00:01 Preparing to unpack .../079-libxvidcore4_2%3a1.3.5-1_amd64.deb ... Setting up libavahi-common-data:amd64 (0.7-3.1ubuntu1.1) ... bms@FortBoyard:~$ # ssh to my machine running Linux Containers Applying documents.0006_auto_20160123_0430... OK Unpacking libaacs0:amd64 (0.9.0-1) ... Stored in directory: /root/.cache/pip/wheels/98/b0/dd/29e28ff615af3dda4c67cab719dd51357597eabff926976b45 64% |████████████████████▋ | 593kB 5.8MB/s eta 0:00:01 Selecting previously unselected package libdjvulibre-text. Preparing to unpack .../130-optipng_0.7.6-1.1_amd64.deb ... Get:107 http://archive.ubuntu.com/ubuntu bionic/main amd64 libwmf0.2-7 amd64 0.2.8.4-12 [150 kB] 100% |████████████████████████████████| 61kB 3.9MB/s lxc-install.sh 100%[==========================================================>] 2.98K --.-KB/s in 0s Username (leave blank to use 'paperless'): Unpacking libxcb-xfixes0:amd64 (1.13-1) ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/jquery/jquery.js' Unpacking libpango-1.0-0:amd64 (1.40.14-1ubuntu0.1) ... 54% [43 libpython3.6-dev 6805 kB/44.8 MB 15%] 4081 kB/s 10s Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/inlines.js' 30% [42 libavutil55 2613 B/190 kB 1%] 47% |███████████████ | 3.4MB 5.2MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/icon-calendar.svg' 34% [Working] 100% [Working] 4630 kB/s 0s Downloading https://files.pythonhosted.org/packages/cb/89/e3687d3ed99bc882793f82634e9824e62499fdfdc4b1ae39e211c5b05017/tzlocal-1.5 Setting up libpoppler-cpp0v5:amd64 (0.62.0-2ubuntu2.5) ... Setting up poppler-data (0.4.8-2) ... 93% |█████████████████████████████▉ | 2.5MB 5.9MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/icon-addlink.svg' Preparing to unpack .../013-libtiff5_4.0.9-5_amd64.deb ... 65% |█████████████████████ | 2.0MB 6.0MB/s eta 0:00:01 20% |██████▌ | 399kB 5.9MB/s eta 0:00:01 Preparing to unpack .../25-libstdc++-7-dev_7.3.0-27ubuntu1~18.04_amd64.deb ... Preparing to unpack .../102-libilmbase12_2.2.0-11ubuntu2_amd64.deb ... Collecting docutils==0.14 (from -r requirements.txt (line 21)) Preparing to unpack .../02-libaprutil1-dbd-sqlite3_1.6.1-2_amd64.deb ... 24% [24 gcc-7 2611 B/7455 kB 0%] Selecting previously unselected package libgcc-7-dev:amd64. 2.tar.gz (Reading database ... 32755 files and directories currently installed.) Applying documents.0021_document_storage_type... OK Running setup.py bdist_wheel for python-levenshtein ... done Selecting previously unselected package libalgorithm-merge-perl. Get:81 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libzvbi-common all 0.2.35-13 [32.1 kB] 48% [Waiting for headers] 4081 kB/s 12s 63% [43 libpython3.6-dev 16.8 MB/44.8 MB 37%] 4081 kB/s 8s Get:18 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libtsan0 amd64 8.2.0-1ubuntu2~18.04 [288 kB] 24% [Waiting for headers] Get:113 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libpoppler73 amd64 0.62.0-2ubuntu2.5 [800 kB] '/usr/share/proftpd/templates/welcome.msg' -> '/srv/ftp/welcome.msg.proftpd-new' Selecting previously unselected package libva-x11-2:amd64. Unpacking libva-drm2:amd64 (2.1.0-3) ... nv-0.9.1-py2.py3-none-any.whl netpbm poppler-data python3-olefile python3-pil tesseract-ocr-eng tesseract-ocr-osd va-driver-all vdpau-driver-all Selecting previously unselected package apache2-data. Preparing to unpack .../035-libaacs0_0.9.0-1_amd64.deb ... update-alternatives: using /usr/bin/identify-im6.q16 to provide /usr/bin/identify-im6 (identify-im6) in auto mode e 2.8; in order to keep installing from binary please use "pip install psycopg2-binary" instead. For details see: <http://initd.org/ 30% |█████████▉ | 604kB 6.0MB/s eta 0:00:01 Collecting pytest-cov==2.6.0 (from -r requirements.txt (line 54)) 66% |█████████████████████▍ | 5.4MB 906kB/s eta 0:00:03 Building dependency tree... 0% Unpacking libtesseract4 (4.00~git2288-10f4998a-2) ... Ignoring python-levenshtein: markers 'extra == "speedup"' don't match your environment 58% [Waiting for headers] 3755 kB/s 7s 97% |███████████████████████████████▏| 6.9MB 37.4MB/s eta 0:00:01 Get:56 http://archive.ubuntu.com/ubuntu bionic/main amd64 libthai-data all 0.1.27-2 [133 kB] Setting up python3-lib2to3 (3.6.7-1~18.04) ... 86% |███████████████████████████▋ | 194kB 5.9MB/s eta 0:00:01 40% |█████████████ | 92kB 5.8MB/s eta 0:00:01 Collecting requests==2.20.0 (from -r requirements.txt (line 67)) Collecting djangorestframework==3.9.0 (from -r requirements.txt (line 19)) Setting up librsvg2-2:amd64 (2.40.20-2) ... Get:22 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libavahi-common3 amd64 0.7-3.1ubuntu1.1 [21.7 kB] Downloading https://files.pythonhosted.org/packages/d1/e6/adb3be5576f5d27c6faa33f1e9fea8fe5dbd9351db12148de948507e352c/prompt_tool y2.py3-none-any.whl (53kB) Setting up libaprutil1-dbd-sqlite3:amd64 (1.6.1-2) ... Preparing to unpack .../17-libtsan0_8.2.0-1ubuntu2~18.04_amd64.deb ... Not uninstalling pyocr at /usr/lib/python3/dist-packages, outside environment /usr 21% [30 libgs9 2611 B/2263 kB 0%] 11% |███▊ | 942kB 3.6MB/s eta 0:00:02 25% [10 Sources 1584 kB/9051 kB 17%] [12 Translation-en 2688 B/91.6 kB 3%] 32% |██████████▌ | 2.7MB 5.9MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/hi.js' 11% |███▋ | 112kB 6.5MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/sorting-icons.svg' Preparing to unpack .../076-libwebpmux3_0.6.1-2_amd64.deb ... Unpacking libxcb-render0:amd64 (1.13-1) ... 48% |███████████████▌ | 3.9MB 6.0MB/s eta 0:00:01 86% |███████████████████████████▋ | 727kB 6.0MB/s eta 0:00:01 Selecting previously unselected package libgsm1:amd64. 38% [Waiting for headers] 3755 kB/s 11s Applying auth.0001_initial... OK Preparing to unpack .../129-netpbm_2%3a10.0-15.3build1_amd64.deb ... Selecting previously unselected package dpkg-dev. Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/ko.js' Unpacking libnss3:amd64 (2:3.35-2ubuntu2.1) ... 55% [Waiting for headers] 3755 kB/s 8s Downloading https://files.pythonhosted.org/packages/d0/43/2160a300e0b77a929a980f36ac7427dcef8f4ddac7a8c21e5a8baedad828/tox-3.5.3-p Preparing to unpack .../31-build-essential_12.4ubuntu1_amd64.deb ... Preparing to unpack .../57-python3-xdg_0.25-4ubuntu1_all.deb ... 56% [Working] 3755 kB/s 8s Preparing to unpack .../36-fakeroot_1.22-2ubuntu1_amd64.deb ... 20.0-py2.py3-none-any.whl (60kB) Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/sk.js' Receiving objects: 93% (5181/5570), 4.67 MiB | 3.10 MiB/s 19% [18 libtsan0 2613 B/288 kB 1%] Preparing to unpack .../091-libbdplus0_0.1.2-2_amd64.deb ... 28% [24 gcc-7 4531 kB/7455 kB 61%] 16% |█████▏ | 1.3MB 4.3MB/s eta 0:00:02 Setting up libpoppler73:amd64 (0.62.0-2ubuntu2.5) ... Setting up make (4.1-9.1ubuntu1) ... 98% |███████████████████████████████▌| 634kB 5.8MB/s eta 0:00:01 Unpacking libmpx2:amd64 (8.2.0-1ubuntu2~18.04) ... Setting up libwmf0.2-7:amd64 (0.2.8.4-12) ... Get:29 http://archive.ubuntu.com/ubuntu bionic-updates/universe Translation-en [175 kB] Copying '/home/paperless/paperless/src/documents/static/documents/img/tiff.png' Requirement already satisfied: jinja2==2.10 in /usr/lib/python3/dist-packages (from -r requirements.txt (line 35)) Downloading https://files.pythonhosted.org/packages/84/71/c8ca4f5bb1e08401b916c68003acf0a0655df935d74d93bf3f3364b310e0/backcall-0. Get:9 http://archive.ubuntu.com/ubuntu bionic/main amd64 libjbig0 amd64 2.1-3.1build1 [26.7 kB] Preparing to unpack .../003-fontconfig_2.12.6-0ubuntu2_amd64.deb ... Setting up libxcb-present0:amd64 (1.13-1) ... 72% |███████████████████████ | 5.1MB 5.9MB/s eta 0:00:01 Applying auth.0003_alter_user_email_max_length... OK Selecting previously unselected package g++. 68% |█████████████████████▉ | 2.1MB 5.9MB/s eta 0:00:01 Preparing to unpack .../023-libcups2_2.2.7-1ubuntu2.3_amd64.deb ... Unpacking python3-pyocr (0.3.0-1) ... Preparing to unpack .../117-libsensors4_1%3a3.4.0-4_amd64.deb ... Reading package lists... 99% Get:33 http://archive.ubuntu.com/ubuntu bionic/main amd64 hicolor-icon-theme all 0.17-2 [9976 B] Preparing to unpack .../083-libbluray2_1%3a1.0.2-3_amd64.deb ... 28% [10 Sources 2262 kB/9051 kB 25%] [Waiting for headers] Unpacking libxcb-present0:amd64 (1.13-1) ... Setting up libjbig0:amd64 (2.1-3.1build1) ... - https://blog.ubuntu.com/2018/12/10/using-gpgpus-with-kubernetes Unpacking tesseract-ocr-eng (4.00~git24-0e00fe6-1.2) ... 22% [10 Sources 895 kB/9051 kB 10%] [11 Packages 188 kB/242 kB 78%] Setting up libopus0:amd64 (1.1.2-1ubuntu1) ... Downloading https://files.pythonhosted.org/packages/19/6c/b2ac85b3f0b48ac968af3741c4f020bf272ab9dabbd1643e9c719441099a/factory_boy Preparing to unpack .../126-libxshmfence1_1.3-1_amd64.deb ... info: Executing deferred 'a2enmod wsgi' for package libapache2-mod-wsgi-py3 12% [3 proftpd-basic 2611 B/2015 kB 0%] Building dependency tree... 50% Selecting previously unselected package python3-crypto. Extracting templates from packages: 42% Enabling module authz_core. Selecting previously unselected package libsensors4:amd64. Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/docs/img/favicon.ico' Unpacking libsnappy1v5:amd64 (1.1.7-1) ... Downloading https://files.pythonhosted.org/packages/3e/6a/a3f909083079d03bde11d06ab23088886bbe25f2c97fbe4bb865e2bf05bc/pytest-suga Preparing to unpack .../28-make_4.1-9.1ubuntu1_amd64.deb ... Username (leave blank to use 'paperless'): Unpacking libpciaccess0:amd64 (0.14-1) ... Unpacking liblcms2-2:amd64 (2.9-1ubuntu0.1) ... Applying documents.0022_auto_20181007_1420... OK Selecting previously unselected package libxcb-shm0:amd64. Preparing to unpack .../057-libthai0_0.1.27-2_amd64.deb ... Unpacking tesseract-ocr-osd (4.00~git24-0e00fe6-1.2) ... Get:99 http://archive.ubuntu.com/ubuntu bionic/main amd64 libdrm-nouveau2 amd64 2.4.91-2 [16.5 kB] Selecting previously unselected package fonts-droid-fallback. Preparing to unpack .../065-libsnappy1v5_1.1.7-1_amd64.deb ... Unpacking librsvg2-2:amd64 (2.40.20-2) ... Unpacking libgraphite2-3:amd64 (1.3.11-2) ... 48% |███████████████▌ | 1.5MB 6.0MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/css/login.css' update-alternatives: using /usr/bin/stream-im6.q16 to provide /usr/bin/stream (stream) in auto mode Preparing to unpack .../046-libcairo2_1.15.10-2_amd64.deb ... Preparing to unpack .../037-libva-drm2_2.1.0-3_amd64.deb ... manpages-dev python-pip-whl python3-crypto python3-dev python3-distutils python3-keyring python3-keyrings.alt python3-lib2to3 Adding user `paperless' to group `ftpupload' ... Collecting ipython==7.1.1 (from -r requirements.txt (line 33)) Setting up python3-wheel (0.30.0-0.2) ... Setting up libpaper1:amd64 (1.1.24+nmu5ubuntu1) ... Selecting previously unselected package libcupsimage2:amd64. 87% |████████████████████████████ | 6.2MB 5.8MB/s eta 0:00:01 Setting up libdjvulibre21:amd64 (3.5.27.1-8) ... Setting up libsnappy1v5:amd64 (1.1.7-1) ... Selecting previously unselected package libalgorithm-diff-perl. Get:26 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libstdc++-7-dev amd64 7.3.0-27ubuntu1~18.04 [1463 kB] 3% |█ | 245kB 1.6MB/s eta 0:00:05 Processing triggers for ufw (0.35-5) ... 3 kB] Unpacking libxcb-dri3-0:amd64 (1.13-1) ... 56% |██████████████████ | 307kB 6.1MB/s eta 0:00:01 Preparing to unpack .../078-libx265-146_2.6-3_amd64.deb ... 15% |█████ | 409kB 5.2MB/s eta 0:00:01 0% [5 Sources store 0 B] [4 InRelease gpgv 74.6 kB] [10 Sources 14.3 kB/9051 kB 0%] [Waiting for headers] Get:39 http://archive.ubuntu.com/ubuntu bionic/main amd64 libxfixes3 amd64 1:5.0.3-1 [10.8 kB] Unpacking libbdplus0:amd64 (0.1.2-2) ... 89% |████████████████████████████▊ | 2.4MB 6.0MB/s eta 0:00:01 Selecting previously unselected package libavutil55:amd64. Selecting previously unselected package python3.6-dev. Unpacking libcc1-0:amd64 (8.2.0-1ubuntu2~18.04) ... Collecting virtualenv==16.1.0 (from -r requirements.txt (line 79)) Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/jquery.init.js' 0% [1 fonts-dejavu-core 2612 B/1041 kB 0%] Selecting previously unselected package libjpeg-turbo8:amd64. Collecting tzlocal==1.5.1 (from -r requirements.txt (line 77)) Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/fonts/fontawesome-webfont.woff' Get:125 http://archive.ubuntu.com/ubuntu bionic/main amd64 libxcb-sync1 amd64 1.13-1 [8808 B] Selecting previously unselected package libcc1-0:amd64. Get:10 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libapache2-mod-wsgi-py3 amd64 4.5.17-1 [87.8 kB] .1.tar.gz 1.0.7.zip (998kB) 0 upgraded, 11 newly installed, 0 to remove and 19 not upgraded. Selecting previously unselected package libsnappy1v5:amd64. Collecting sphinx==1.8.1 (from -r requirements.txt (line 70)) 83% [Working] 4630 kB/s 2s Preparing to unpack .../105-libopenexr22_2.2.0-11.1ubuntu1_amd64.deb ... 60% |███████████████████▎ | 389kB 5.8MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/css/bootstrap-theme.min.css' 75% [10 Sources store 0 B] Unpacking libgs9:amd64 (9.26~dfsg+0-0ubuntu0.18.04.3) ... Selecting previously unselected package fakeroot. Downloading https://files.pythonhosted.org/packages/89/e6/b5a1de8b0cc4e07ca1b305a4fcc3f9806025c1b651ea302646341222f88b/pexpect-4.6 6% |██▏ | 481kB 5.9MB/s eta 0:00:02 91% [18 Packages store 0 B] Setting up libpaper-utils (1.1.24+nmu5ubuntu1) ... 100% |████████████████████████████████| 92kB 3.7MB/s Downloading https://files.pythonhosted.org/packages/bc/2a/61a8f9719bd6df5b421abd91740cb0595fc3c17b28eaf89fe4f144472ca6/psycopg2-2. Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/gl.js' Get:114 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libpoppler-cpp0v5 amd64 0.62.0-2ubuntu2.5 [28.0 kB] Stored in directory: /root/.cache/pip/wheels/75/fc/26/357bc139b726977ab08817308ce76d96a26bdbe2329a43b15a 27% [13 Packages store 0 B] [10 Sources 2085 kB/9051 kB 23%] [Waiting for headers] Reading package lists... 96% Found existing installation: attrs 17.4.0 52% [Waiting for headers] 3755 kB/s 8s 6% [Working] 2% |▉ | 194kB 6.0MB/s eta 0:00:02 99% |███████████████████████████████▉| 993kB 6.0MB/s eta 0:00:01 Preparing to unpack .../proftpd-basic_1.3.5e-1build1_amd64.deb ... Get:94 http://archive.ubuntu.com/ubuntu bionic/main amd64 libdjvulibre-text all 3.5.27.1-8 [49.1 kB] 86% |███████████████████████████▊ | 1.7MB 5.9MB/s eta 0:00:01 83% |██████████████████████████▋ | 829kB 5.9MB/s eta 0:00:01 0% [2 InRelease gpgv 83.2 kB] Preparing to unpack .../07-gcc-7-base_7.3.0-27ubuntu1~18.04_amd64.deb ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/nb.js' 9% |███ | 778kB 3.4MB/s eta 0:00:03 Setting up libtiff5:amd64 (4.0.9-5) ... Selecting previously unselected package hicolor-icon-theme. Preparing to unpack .../019-fonts-noto-mono_20171026-2_all.deb ... Get:95 http://archive.ubuntu.com/ubuntu bionic/main amd64 libdjvulibre21 amd64 3.5.27.1-8 [559 kB] 50% |████████████████▎ | 1.0MB 5.9MB/s eta 0:00:01 Preparing to unpack .../018-poppler-data_0.4.8-2_all.deb ... 93% |██████████████████████████████ | 7.6MB 6.0MB/s eta 0:00:01 Selecting previously unselected package python3-distutils. Unpacking python3-keyring (10.6.0-1) ... 47% [Waiting for headers] 4081 kB/s 12s Get:8 http://archive.ubuntu.com/ubuntu bionic/main amd64 libfftw3-double3 amd64 3.3.7-1 [735 kB] 14% |████▋ | 1.2MB 3.9MB/s eta 0:00:02 Setting up python3-secretstorage (2.3.1-2) ... Setting up python3-keyrings.alt (3.0-1) ... Get:131 http://archive.ubuntu.com/ubuntu bionic/main amd64 optipng amd64 0.7.6-1.1 [83.4 kB] 24% |████████ | 2.0MB 5.5MB/s eta 0:00:02 Warning: The home dir /run/proftpd you specified can't be accessed: No such file or directory 40% |█████████████ | 3.3MB 6.0MB/s eta 0:00:01 Resolving deltas: 8% (293/3560) 2% [Waiting for headers] Unpacking optipng (0.7.6-1.1) ... Resolving raw.githubusercontent.com (raw.githubusercontent.com)... Found existing installation: pyocr 0.3.0 30% [Waiting for headers] Get:141 http://archive.ubuntu.com/ubuntu bionic/main amd64 vdpau-driver-all amd64 1.1.1-3ubuntu1 [4674 B] Selecting previously unselected package libpython3.6-dev:amd64. Get:100 http://archive.ubuntu.com/ubuntu bionic/main amd64 libdrm-radeon1 amd64 2.4.91-2 [21.7 kB] 52% |████████████████▋ | 112kB 6.4MB/s eta 0:00:01 Get:5 http://archive.ubuntu.com/ubuntu bionic/main amd64 libc-dev-bin amd64 2.27-3ubuntu1 [71.8 kB] 98% |███████████████████████████████▌| 61kB 6.3MB/s eta 0:00:01 Running setup.py bdist_wheel for pyocr ... done Get:24 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 gcc-7 amd64 7.3.0-27ubuntu1~18.04 [7455 kB] Setting up libfftw3-double3:amd64 (3.3.7-1) ... Preparing to unpack .../051-libopus0_1.1.2-1ubuntu1_amd64.deb ... Get:9 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 apache2 amd64 2.4.29-1ubuntu4.5 [95.1 kB] Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/search.svg' 28% [Working] 88% |████████████████████████████▍ | 61kB 5.1MB/s eta 0:00:01 0.6.0-py2.py3-none-any.whl 31% [Waiting for headers] Setting up libpciaccess0:amd64 (0.14-1) ... 56% [Waiting for headers] 3755 kB/s 8s Get:53 http://archive.ubuntu.com/ubuntu bionic-updates/universe amd64 python3-pip all 9.0.1-2.3~ubuntu1 [114 kB] Preparing to unpack .../06-apache2-utils_2.4.29-1ubuntu4.5_amd64.deb ... Running setup.py bdist_wheel for tzlocal ... done Downloading https://files.pythonhosted.org/packages/36/fa/08e9e6e0e3cbd1d362c3bbee8d01d0aedb2155c4ac112b19ef3cae8eed8d/docutils-0. 90% [Waiting for headers] 4611 kB/s 1s Copying '/home/paperless/paperless/src/paperless/static/paperless/img/logo-light.png' Setting up libaacs0:amd64 (0.9.0-1) ... 65% |█████████████████████ | 501kB 5.9MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/select2.full.js' Setting up build-essential (12.4ubuntu1) ... Enabling conf security. Collecting certifi==2018.10.15 (from -r requirements.txt (line 8)) Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.svg' Get:35 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 imagemagick amd64 8:6.9.7.4+dfsg-16ubuntu6.4 [14.2 kB] Setting up liblua5.2-0:amd64 (5.2.4-1.1build1) ... Downloading https://files.pythonhosted.org/packages/02/75/d041ed00994fbac4c5183e6f4bf6c906506bef8da7a57ef3fc825f171020/pytest-3.9. 41% [29 make 4061 B/154 kB 3%] 4081 kB/s 12s Collecting pytest-env==0.6.2 (from -r requirements.txt (line 56)) Setting up fonts-droid-fallback (1:6.0.1r16-1.1) ... Collecting ptyprocess==0.6.0 (from -r requirements.txt (line 48)) Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/css/fonts.css' Receiving objects: 18% (1003/5570) Stored in directory: /root/.cache/pip/wheels/a9/c0/1e/32b694059fabeb7a9bcd2c65091ca875cd78ec1f54f47ea03b Get:37 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libva2 amd64 2.1.0-3 [47.6 kB] Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/css/changelists.css' 23% [6 apache2-bin 2611 B/1071 kB 0%] 85% |███████████████████████████▎ | 788kB 5.9MB/s eta 0:00:01 (Reading database ... 33040 files and directories currently installed.) Reading package lists... 56% mesa-vdpau-drivers netpbm optipng poppler-data python3-olefile python3-pil python3-pyocr tesseract-ocr tesseract-ocr-eng 22% |███████ | 1.6MB 5.9MB/s eta 0:00:01 21% [Waiting for headers] Preparing to unpack .../140-vdpau-driver-all_1.1.1-3ubuntu1_amd64.deb ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/th.js' Preparing to unpack .../108-libnetpbm10_2%3a10.0-15.3build1_amd64.deb ... update-alternatives: using /usr/bin/import-im6.q16 to provide /usr/bin/import (import) in auto mode Downloading https://files.pythonhosted.org/packages/d7/ca/3c74396a9ed8a4cfab5459800edeef9a1269591cb21f5a49bd71a49c5fa2/filelock-3. 92% [18 Packages store 0 B] 29% [10 Sources 2531 kB/9051 kB 28%] [Waiting for headers] Extracting templates from packages: 63% HTTP request sent, awaiting response... 2019-01-20 19:50:06 (35.8 MB/s) - ‘lxc-install.sh’ saved [3054/3054] 93% [18 Packages store 0 B] [27 Translation-en 0 B/182 kB 0%] 81% |██████████████████████████ | 1.6MB 6.0MB/s eta 0:00:01 44% [83 libavcodec57 4059 B/4592 kB 0%] 3755 kB/s 10s Get:54 http://archive.ubuntu.com/ubuntu bionic/main amd64 libgdk-pixbuf2.0-common all 2.36.11-2 [4536 B] 43% [82 libzvbi0 4061 B/235 kB 2%] 3755 kB/s 10s 96% [Waiting for headers] 4944 kB/s 0s 51% |████████████████▍ | 4.1MB 6.0MB/s eta 0:00:01 Get:88 http://archive.ubuntu.com/ubuntu bionic/main amd64 libvorbisfile3 amd64 1.3.5-4.2 [16.0 kB] .9.0-py2.py3-none-any.whl (112kB) Setting up gcc (4:7.3.0-3ubuntu2.1) ... Setting up libavformat57:amd64 (7:3.4.4-0ubuntu0.18.04.1) ... Unpacking libmpc3:amd64 (1.1.0-1) ... Preparing to unpack .../066-libspeex1_1.2~rc1.2-1ubuntu2_amd64.deb ... Unpacking python3.6-venv (3.6.7-1~18.04) ... Preparing to unpack .../071-libvorbis0a_1.3.5-4.2_amd64.deb ... 7% [11 liblcms2-2 2613 B/139 kB 2%] Setting up libjbig2dec0:amd64 (0.13-6) ... Preparing to unpack .../35-libfakeroot_1.22-2ubuntu1_amd64.deb ... bms@pingu : ~ $ 5% [7 libc6-dev 4059 B/2587 kB 0%] Applying contenttypes.0001_initial... OK Enabling module authn_core. 24% [32 gsfonts 1163 B/3120 kB 0%] Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/fonts/Roboto-Regular-webfont.woff' Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/inlines.min.js' update-alternatives: using /usr/bin/fakeroot-sysv to provide /usr/bin/fakeroot (fakeroot) in auto mode 92% [Waiting for headers] 42% [31 dpkg-dev 2613 B/608 kB 0%] 4081 kB/s 12s 100% |████████████████████████████████| 235kB 2.8MB/s Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/es.js' Get:111 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libnss3 amd64 2:3.35-2ubuntu2.1 [1135 kB] Running setup.py bdist_wheel for pytest-sugar ... - 4% [Waiting for headers] 81% |██████████████████████████ | 6.6MB 6.0MB/s eta 0:00:01 update-alternatives: using /usr/bin/composite-im6.q16 to provide /usr/bin/composite (composite) in auto mode Collecting pytest==3.9.3 (from -r requirements.txt (line 60)) 31% [Working] Enabling module mpm_event. Regenerating fonts cache... done. 80% |█████████████████████████▉ | 5.7MB 5.9MB/s eta 0:00:01 Unpacking libgcc-7-dev:amd64 (7.3.0-27ubuntu1~18.04) ... Get:16 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libmagickcore-6.q16-3 amd64 8:6.9.7.4+dfsg-16ubuntu6.4 [1616 kB] 100% |████████████████████████████████| 61kB 4.5MB/s 97% [19 Translation-en store 0 B] 4229 kB/s 0s Setting up mesa-va-drivers:amd64 (18.0.5-0ubuntu0~18.04.1) ... Setting up libjpeg8:amd64 (8c-2ubuntu8) ... Preparing to unpack .../036-libva2_2.1.0-3_amd64.deb ... 4% [Working] Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/fonts/Roboto-Light-webfont.woff' Get:76 http://archive.ubuntu.com/ubuntu bionic/main amd64 libwebp6 amd64 0.6.1-2 [185 kB] Get:36 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libaacs0 amd64 0.9.0-1 [51.4 kB] Selecting previously unselected package python3-venv. 100% |████████████████████████████████| 768kB 1.2MB/s Preparing to unpack .../050-libopenjp2-7_2.3.0-1_amd64.deb ... 52% [91 libavformat57 1165 B/949 kB 0%] 3755 kB/s 8s 96% |██████████████████████████████▉ | 7.8MB 5.9MB/s eta 0:00:01 Preparing to unpack .../002-libfontconfig1_2.12.6-0ubuntu2_amd64.deb ... 29% [Waiting for headers] Unpacking libmp3lame0:amd64 (3.100-2) ... Selecting previously unselected package cpp-7. 14% [Waiting for headers] Collecting alabaster==0.7.12 (from -r requirements.txt (line 2)) Unpacking i965-va-driver:amd64 (2.1.0-0ubuntu1) ... 44% [Waiting for headers] 4081 kB/s 12s Selecting previously unselected package build-essential. Running setup.py bdist_wheel for termcolor ... - Receiving objects: 100% (5570/5570), 6.39 MiB | 3.49 MiB/s, done. Selecting previously unselected package libmpc3:amd64. Reading package lists... 97% * Documentation: https://help.ubuntu.com Collecting django-extensions==2.1.3 (from -r requirements.txt (line 16)) 100% |████████████████████████████████| 348kB 2.2MB/s 85% |███████████████████████████▎ | 6.9MB 5.8MB/s eta 0:00:01 Setting up tesseract-ocr-osd (4.00~git24-0e00fe6-1.2) ... Collecting pyparsing==2.3.0 (from -r requirements.txt (line 53)) Get:96 http://archive.ubuntu.com/ubuntu bionic/main amd64 libdrm-amdgpu1 amd64 2.4.91-2 [19.0 kB] Preparing to unpack .../proftpd-doc_1.3.5e-1build1_all.deb ... 92% [135 tesseract-ocr-eng 2611 B/1588 kB 0%] 4630 kB/s 1s Running setup.py bdist_wheel for termcolor ... done Done. Setting up libgraphite2-3:amd64 (1.3.11-2) ... 1% |▌ | 122kB 1.1MB/s eta 0:00:08 Selecting previously unselected package libthai-data. Unpacking libavcodec57:amd64 (7:3.4.4-0ubuntu0.18.04.1) ... Setting up python3-olefile (0.45.1-1) ... Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/docs/js/api.js' Get:1 http://archive.ubuntu.com/ubuntu bionic/main amd64 fonts-dejavu-core all 2.37-1 [1041 kB] Selecting previously unselected package liblua5.2-0:amd64. Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/ja.js' 12% |████ | 880kB 5.8MB/s eta 0:00:02 .5.1-py2.py3-none-any.whl Not uninstalling certifi at /usr/lib/python3/dist-packages, outside environment /usr Setting up libcupsfilters1:amd64 (1.20.2-0ubuntu3) ... 84% [Waiting for headers] 4630 kB/s 2s 92% |█████████████████████████████▋ | 1.8MB 6.0MB/s eta 0:00:01 Applying documents.0018_auto_20170715_1712... OK Get:8 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 apache2-data all 2.4.29-1ubuntu4.5 [160 kB] 58% |██████████████████▉ | 4.8MB 5.8MB/s eta 0:00:01 Selecting previously unselected package python3-lib2to3. 89% [Waiting for headers] 4630 kB/s 1s Resolving deltas: 35% (1255/3560) 0 upgraded, 141 newly installed, 0 to remove and 19 not upgraded. Selecting previously unselected package python3-olefile. i965-va-driver imagemagick-6-common imagemagick-6.q16 libaacs0 libavahi-client3 libavahi-common-data libavahi-common3 Unpacking apache2 (2.4.29-1ubuntu4.5) ... update-alternatives: using /usr/bin/animate-im6.q16 to provide /usr/bin/animate-im6 (animate-im6) in auto mode Selecting previously unselected package libgs9:amd64. 18% |██████ | 1.5MB 4.8MB/s eta 0:00:02 37% [Waiting for headers] Unpacking libva2:amd64 (2.1.0-3) ... Preparing to unpack .../120-libx11-xcb1_2%3a1.6.4-3ubuntu0.1_amd64.deb ... Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/js/default.js' Get:110 http://archive.ubuntu.com/ubuntu bionic/main amd64 libnspr4 amd64 2:4.18-1ubuntu1 [112 kB] 68% [105 libllvm6.0 8630 kB/14.5 MB 59%] 3755 kB/s 5s Selecting previously unselected package libtiff5:amd64. Setting up liblqr-1-0:amd64 (0.4.2-2.1) ... libgomp1 libgraphite2-3 libgs9 libgs9-common libgsm1 libharfbuzz0b libijs-0.35 libilmbase12 libjbig0 libjbig2dec0 libjpeg-turbo8 61% |███████████████████▋ | 5.0MB 5.7MB/s eta 0:00:01 -py2.py3-none-any.whl (506kB) .1-py3-none-any.whl (764kB) Setting up librsvg2-common:amd64 (2.40.20-2) ... Selecting previously unselected package libpython3-dev:amd64. Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.woff2' 90% [18 Packages store 0 B] Preparing to unpack .../47-python3.6-dev_3.6.7-1~18.04_amd64.deb ... 60% |███████████████████▍ | 204kB 6.0MB/s eta 0:00:01 9% |███ | 686kB 5.9MB/s eta 0:00:02 92% [18 Packages store 0 B] [26 Packages 2687 B/489 kB 1%] 38% [Waiting for headers] 3755 kB/s 11s Selecting previously unselected package libpixman-1-0:amd64. Unpacking libvpx5:amd64 (1.7.0-3) ... Preparing to unpack .../004-fonts-droid-fallback_1%3a6.0.1r16-1.1_all.deb ... Setting up libavutil55:amd64 (7:3.4.4-0ubuntu0.18.04.1) ... Preparing to unpack .../111-libpaper-utils_1.1.24+nmu5ubuntu1_amd64.deb ... """) Unpacking imagemagick-6-common (8:6.9.7.4+dfsg-16ubuntu6.4) ... Unpacking apache2-utils (2.4.29-1ubuntu4.5) ... 98% [139 i965-va-driver 2613 B/925 kB 0%] 4630 kB/s 0s .0-py2.py3-none-any.whl (57kB) Get:51 http://archive.ubuntu.com/ubuntu bionic/main amd64 python3-keyring all 10.6.0-1 [26.7 kB] Enabling conf other-vhosts-access-log. 5% |█▊ | 389kB 5.8MB/s eta 0:00:02 Collecting docopt==0.6.2 (from -r requirements.txt (line 20)) 4% [6 linux-libc-dev 2612 B/1010 kB 0%] 87% |████████████████████████████ | 2.7MB 5.8MB/s eta 0:00:01 Selecting previously unselected package libxcb-xfixes0:amd64. 96% [18 Packages store 0 B] 76% [106 libopenexr22 2613 B/560 kB 0%] 3755 kB/s 4s Setting up binutils-common:amd64 (2.30-21ubuntu1~18.04) ... 11% [Working] Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.ttf' 84% |███████████████████████████ | 2.6MB 6.0MB/s eta 0:00:01 Applying auth.0007_alter_validators_add_error_messages... OK Extracting templates from packages: 51% exit Get:28 http://archive.ubuntu.com/ubuntu bionic/main amd64 libpaper1 amd64 1.1.24+nmu5ubuntu1 [13.6 kB] Selecting previously unselected package libquadmath0:amd64. 52% [90 libssh-gcrypt-4 2613 B/171 kB 2%] 3755 kB/s 8s Get:65 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libshine3 amd64 3.1.1-1 [22.9 kB] Setting up liblcms2-2:amd64 (2.9-1ubuntu0.1) ... 57% [43 libpython3.6-dev 9668 kB/44.8 MB 22%] 4081 kB/s 9s Memory usage: 7% IP address for lxdbr0: 10.69.143.1 Applying documents.0010_log... OK Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/css/base.css' Setting up hicolor-icon-theme (0.17-2) ... 39% [27 g++-7 5580 kB/7570 kB 74%] 4081 kB/s 13s Get:17 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libmagickwand-6.q16-3 amd64 8:6.9.7.4+dfsg-16ubuntu6.4 [293 kB] Get:47 http://archive.ubuntu.com/ubuntu bionic/main amd64 python3-crypto amd64 2.6.1-8ubuntu2 [244 kB] Get:15 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libatomic1 amd64 8.2.0-1ubuntu2~18.04 [9064 B] util-2.7.5-py2.py3-none-any.whl (225kB) Unpacking libxrender1:amd64 (1:0.9.10-1) ... 15% |████▉ | 1.1MB 5.9MB/s eta 0:00:02 40% |████████████▉ | 307kB 5.8MB/s eta 0:00:01 gcc-7-base libalgorithm-diff-perl libalgorithm-diff-xs-perl libalgorithm-merge-perl libasan4 libatomic1 libbinutils libc-dev-bin Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/icon-yes.svg' 95% |██████████████████████████████▌ | 7.7MB 5.9MB/s eta 0:00:01 Preparing to unpack .../03-libaprutil1-ldap_1.6.1-2_amd64.deb ... Processing triggers for ureadahead (0.100.0-20) ... 80% |█████████████████████████▋ | 6.5MB 6.0MB/s eta 0:00:01 1% [Working] 71% [43 libpython3.6-dev 24.8 MB/44.8 MB 55%] 4611 kB/s 5s Running setup.py bdist_wheel for tzlocal ... - 8% [Waiting for headers] 53% [Waiting for headers] 3755 kB/s 8s Setting up libtheora0:amd64 (1.1.1+dfsg.1-14) ... Unpacking libbluray2:amd64 (1:1.0.2-3) ... Setting up libavcodec57:amd64 (7:3.4.4-0ubuntu0.18.04.1) ... Selecting previously unselected package liblsan0:amd64. Selecting previously unselected package libaacs0:amd64. 29% [Waiting for headers] Receiving objects: 76% (4234/5570), 4.67 MiB | 3.10 MiB/s update-alternatives: using /usr/bin/convert-im6.q16 to provide /usr/bin/convert-im6 (convert-im6) in auto mode Selecting previously unselected package libmpg123-0:amd64. bms@pingu : ~ $ exi 63% |████████████████████▍ | 1.7MB 6.0MB/s eta 0:00:01 Selecting previously unselected package liblept5. mmer-1.2.1-py2.py3-none-any.whl (64kB) Get:97 http://archive.ubuntu.com/ubuntu bionic/main amd64 libpciaccess0 amd64 0.14-1 [17.9 kB] Unpacking libdjvulibre-text (3.5.27.1-8) ... Selecting previously unselected package va-driver-all:amd64. Unpacking libpangoft2-1.0-0:amd64 (1.40.14-1ubuntu0.1) ... 39% [10 Sources 5845 kB/9051 kB 65%] Selecting previously unselected package libmpx2:amd64. Downloading https://files.pythonhosted.org/packages/10/e0/27850afcb1683561c42db7c20e897cd5eb75b24843d574d8ec1389da4d49/fuzzywuzzy- 92% |█████████████████████████████▊ | 471kB 5.9MB/s eta 0:00:01 Collecting decorator==4.3.0 (from -r requirements.txt (line 13)) 99% |███████████████████████████████▊| 757kB 6.4MB/s eta 0:00:01 Preparing to unpack .../22-libgcc-7-dev_7.3.0-27ubuntu1~18.04_amd64.deb ... 0.6.2.tar.gz Get:30 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libdpkg-perl all 1.19.0.5ubuntu2.1 [211 kB] Email address: e 64% |████████████████████▊ | 5.3MB 5.9MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/selector-icons.svg' Unpacking libjpeg8:amd64 (8c-2ubuntu8) ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/timeparse.js' Applying auth.0002_alter_permission_name_max_length... OK Setting up python3-pil:amd64 (5.1.0-1) ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/ca.js' Collecting pickleshare==0.7.5 (from -r requirements.txt (line 43)) 22% [Waiting for headers] .1 pytest-xdist-1.24.0 python-dateutil-2.7.5 python-dotenv-0.9.1 python-gnupg-0.4.3 python-levenshtein-0.12.0 pytz-2018.7 regex-2018 Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/css/bootstrap-tweaks.css' Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/icon-alert.svg' Fetched 1818 kB in 1s (2310 kB/s) Setting up libshine3:amd64 (3.1.1-1) ... -py2.py3-none-any.whl (8.1MB) Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/xregexp/xregexp.js' Setting up libtwolame0:amd64 (0.3.13-3) ... Preparing to unpack .../43-libpython3-dev_3.6.7-1~18.04_amd64.deb ... Get:137 http://archive.ubuntu.com/ubuntu bionic/universe amd64 tesseract-ocr amd64 4.00~git2288-10f4998a-2 [218 kB] Downloading https://files.pythonhosted.org/packages/6c/9d/c0feec696b815708354a2fd06ae0f51330a15043822a29bc8be2f185d9fe/Django-2.0. 7% |██▍ | 614kB 2.8MB/s eta 0:00:03 39% [Waiting for headers] 3755 kB/s 11s 18% [29 libgs9-common 2852 kB/5094 kB 56%] ib_websupport-1.1.0-py2.py3-none-any.whl Get:60 http://archive.ubuntu.com/ubuntu bionic/main amd64 libgraphite2-3 amd64 1.3.11-2 [78.7 kB] Selecting previously unselected package libitm1:amd64. Unpacking apache2-data (2.4.29-1ubuntu4.5) ... Preparing to unpack .../38-libalgorithm-diff-xs-perl_0.04-5_amd64.deb ... py_forms-1.7.2-py2.py3-none-any.whl (105kB) 90% |█████████████████████████████ | 583kB 5.9MB/s eta 0:00:01 Setting up libxcb-render0:amd64 (1.13-1) ... Unpacking libx11-xcb1:amd64 (2:1.6.4-3ubuntu0.1) ... 81% |██████████████████████████ | 2.5MB 6.0MB/s eta 0:00:01 20% [Waiting for headers] Selecting previously unselected package libgomp1:amd64. 14% [Waiting for headers] 35% [Working] 54% [93 libcupsfilters1 2613 B/108 kB 2%] 3755 kB/s 8s Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/gis/move_vertex_off.svg' Selecting previously unselected package libvorbis0a:amd64. 64% [10 Sources store 0 B] [18 Packages 5094 kB/8570 kB 59%] hon-genutils-0.2.0 jedi-0.13.1 langdetect-1.0.7 more-itertools-4.3.0 packaging-18.0 parso-0.3.1 pdftotext-2.1.1 pexpect-4.6.0 pickle nshtein-0.12.0.tar.gz (48kB) 33% [51 libopenjp2-7 2613 B/145 kB 2%] Setting up libgdk-pixbuf2.0-0:amd64 (2.36.11-2) ... 9-py3-none-any.whl (7.1MB) 98% [54 python3-setuptools 4061 B/248 kB 2%] 4944 kB/s 0s nsions-2.1.3-py2.py3-none-any.whl (216kB) 0.15.0-py2.py3-none-any.whl 36% |███████████▌ | 2.6MB 6.0MB/s eta 0:00:01 Setting up libpoppler-dev:amd64 (0.62.0-2ubuntu2.5) ... 86% |███████████████████████████▋ | 7.0MB 6.0MB/s eta 0:00:01 Get:130 http://archive.ubuntu.com/ubuntu bionic/main amd64 netpbm amd64 2:10.0-15.3build1 [1017 kB] 61% |███████████████████▊ | 1.9MB 6.0MB/s eta 0:00:01 Setting up libthai0:amd64 (0.1.27-2) ... Fetched 3273 kB in 1s (3711 kB/s) Copying '/usr/local/lib/python3.6/dist-packages/django_extensions/static/django_extensions/js/jquery.bgiframe.js' 78% [Waiting for headers] 3755 kB/s 3s 81% [119 libtesseract4 2611 B/1163 kB 0%] 4630 kB/s 2s 91% |█████████████████████████████▍ | 1.8MB 5.9MB/s eta 0:00:01 Adding new user `ftp' (UID 112) with group `nogroup' ... Copying '/home/paperless/paperless/src/documents/static/documents/img/jpg.png' Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/id.js' 36% |███████████▊ | 3.0MB 6.0MB/s eta 0:00:01 Selecting previously unselected package libllvm6.0:amd64. Setting up vdpau-driver-all:amd64 (1.1.1-3ubuntu1) ... Setting up libmpx2:amd64 (8.2.0-1ubuntu2~18.04) ... 95% |██████████████████████████████▋ | 204kB 6.0MB/s eta 0:00:01 Selecting previously unselected package python3-pyocr. Unpacking libshine3:amd64 (3.1.1-1) ... 16% |█████▍ | 92kB 5.8MB/s eta 0:00:01 Unpacking libapache2-mod-wsgi-py3 (4.5.17-1) ... Fetched 64.4 MB in 15s (4211 kB/s) Selecting previously unselected package netpbm. 90% [18 Packages store 0 B] [20 Packages 4135 B/151 kB 3%] Requirement already satisfied: markupsafe==1.0 in /usr/lib/python3/dist-packages (from -r requirements.txt (line 37)) Get:68 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libsoxr0 amd64 0.1.2-3 [65.9 kB] Selecting previously unselected package libdjvulibre21:amd64. 99% |███████████████████████████████▊| 8.0MB 5.8MB/s eta 0:00:01 Get:6 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 linux-libc-dev amd64 4.15.0-43.46 [1010 kB] Setting up libijs-0.35:amd64 (0.35-13) ... Setting up libmagickwand-6.q16-3:amd64 (8:6.9.7.4+dfsg-16ubuntu6.4) ... .11.2 requests-2.20.0 snowballstemmer-1.2.1 sphinx-1.8.1 sphinxcontrib-websupport-1.1.0 termcolor-1.1.0 text-unidecode-1.2 toml-0.10 Preparing to unpack .../006-libgomp1_8.2.0-1ubuntu2~18.04_amd64.deb ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/popup_response.js' Running setup.py bdist_wheel for regex ... done Downloading https://files.pythonhosted.org/packages/4a/87/76ead690afc4c7710012ede242537cd9807dde9de6299e65d075925c0b02/python_gnup Collecting jedi==0.13.1 (from -r requirements.txt (line 34)) Setting up libsoxr0:amd64 (0.1.2-3) ... Setting up libjpeg-turbo8:amd64 (1.5.2-0ubuntu5.18.04.1) ... Get:4 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 binutils amd64 2.30-21ubuntu1~18.04 [3392 B] Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/cs.js' 94% [Waiting for headers] 4630 kB/s 0s Get:43 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libpython3.6-dev amd64 3.6.7-1~18.04 [44.8 MB] 100% |████████████████████████████████| 153kB 3.3MB/s 97% [18 Packages store 0 B] 1.0.tar.gz Setting up libwebpmux3:amd64 (0.6.1-2) ... 92% |█████████████████████████████▌ | 81kB 6.1MB/s eta 0:00:01 Enabling conf serve-cgi-bin. 74% |████████████████████████ | 481kB 6.0MB/s eta 0:00:01 Running setup.py bdist_wheel for pdftotext ... | 33% |██████████▋ | 2.4MB 6.0MB/s eta 0:00:01 Setting up unpaper (6.1-2) ... Package apache2 is not configured yet. Will defer actions by package libapache2-mod-wsgi-py3. 53% |█████████████████ | 1.4MB 6.0MB/s eta 0:00:01 Setting up tesseract-ocr-eng (4.00~git24-0e00fe6-1.2) ... Get:22 http://archive.ubuntu.com/ubuntu bionic-updates/main Sources [231 kB] -2.4.0-py2.py3-none-any.whl (62kB) update-alternatives: using /usr/bin/mogrify-im6.q16 to provide /usr/bin/mogrify-im6 (mogrify-im6) in auto mode Setting up libpangoft2-1.0-0:amd64 (1.40.14-1ubuntu0.1) ... Get:7 http://archive.ubuntu.com/ubuntu bionic/main amd64 libc6-dev amd64 2.27-3ubuntu1 [2587 kB] 2% [3 libfontconfig1 4061 B/137 kB 3%] libx11-xcb1 libx264-152 libx265-146 libxcb-dri2-0 libxcb-dri3-0 libxcb-present0 libxcb-render0 libxcb-shm0 libxcb-sync1 Unpacking python3-setuptools (39.0.1-2) ... Selecting previously unselected package libbinutils:amd64. Preparing to unpack .../50-python3-keyring_10.6.0-1_all.deb ... 16.1.0-py2.py3-none-any.whl (1.9MB) Selecting previously unselected package libilmbase12:amd64. Get:118 http://archive.ubuntu.com/ubuntu bionic/main amd64 libsensors4 amd64 1:3.4.0-4 [28.8 kB] Copying '/home/paperless/paperless/src/documents/static/documents/img/image.png' Selecting previously unselected package libpoppler-cpp-dev:amd64. Setting up libmpc3:amd64 (1.1.0-1) ... Creating home directory `/home/ftpupload' ... Get:9 http://security.ubuntu.com/ubuntu bionic-security/universe Sources [29.4 kB] 56% [10 Sources store 0 B] [18 Packages 2234 kB/8570 kB 26%] Unpacking dh-python (3.20180325ubuntu2) ... 100% |████████████████████████████████| 1.9MB 510kB/s Running setup.py bdist_wheel for pytest-sugar ... done Setting up liblept5 (1.75.3-3) ... Receiving objects: 23% (1282/5570), 676.01 KiB | 1.30 MiB/s Get:40 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libva-x11-2 amd64 2.1.0-3 [11.5 kB] Unpacking libdatrie1:amd64 (0.2.10-7) ... Setting up libspeex1:amd64 (1.2~rc1.2-1ubuntu2) ... 97% [19 Translation-en store 0 B] 4229 kB/s 0s Get:40 http://archive.ubuntu.com/ubuntu bionic/main amd64 libalgorithm-merge-perl all 0.08-3 [12.0 kB] 30% |█████████▊ | 2.5MB 5.8MB/s eta 0:00:01 72% |███████████████████████▍ | 614kB 6.0MB/s eta 0:00:01 Unpacking manpages-dev (4.15-1) ... After this operation, 247 MB of additional disk space will be used. 32% [Waiting for headers] Get:123 http://archive.ubuntu.com/ubuntu bionic/main amd64 libxcb-dri3-0 amd64 1.13-1 [6536 B] Get:112 http://archive.ubuntu.com/ubuntu bionic/main amd64 libpaper-utils amd64 1.1.24+nmu5ubuntu1 [8170 B] 97% |███████████████████████████████▏| 348kB 6.1MB/s eta 0:00:01 Downloading https://files.pythonhosted.org/packages/f8/0e/2365ddc010afb3d79147f1dd544e5ee24bf4ece58ab99b16fbb465ce6dc0/pytz-2018.7 33% [52 libopus0 2613 B/159 kB 2%] 3% |█▏ | 112kB 6.4MB/s eta 0:00:01 Preparing to unpack .../113-libpoppler-cpp0v5_0.62.0-2ubuntu2.5_amd64.deb ... 79% |█████████████████████████▎ | 604kB 5.9MB/s eta 0:00:01 8% [16 libmagickcore-6.q16-3 2611 B/1616 kB 0%] Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.woff' 24% |████████ | 1.8MB 6.0MB/s eta 0:00:01 Preparing to unpack .../038-libxfixes3_1%3a5.0.3-1_amd64.deb ... Setting up libxcb-xfixes0:amd64 (1.13-1) ... Setting up python3-venv (3.6.7-1~18.04) ... Setting up python3.6-dev (3.6.7-1~18.04) ... 6% [Waiting for headers] Setting up libx11-xcb1:amd64 (2:1.6.4-3ubuntu0.1) ... Get:27 http://archive.ubuntu.com/ubuntu bionic-updates/main Translation-en [182 kB] Get:117 http://archive.ubuntu.com/ubuntu bionic/main amd64 librsvg2-common amd64 2.40.20-2 [5124 B] Unpacking libvorbisenc2:amd64 (1.3.5-4.2) ... Setting up libdrm-intel1:amd64 (2.4.91-2) ... 78% [111 libnss3 2611 B/1135 kB 0%] 3755 kB/s 3s Downloading https://files.pythonhosted.org/packages/7c/17/9b7b6cddfd255388b58c61e25b091047f6814183e1d63741c8df8dcd65a2/virtualenv- Collecting inotify-simple==1.1.8 (from -r requirements.txt (line 31)) 9% |███▏ | 81kB 4.8MB/s eta 0:00:01 Unpacking python-pip-whl (9.0.1-2.3~ubuntu1) ... Setting up libchromaprint1:amd64 (1.4.3-1) ... Unpacking libx265-146:amd64 (2.6-3) ... Selecting previously unselected package python3-wheel. Selecting previously unselected package libgraphite2-3:amd64. Not creating home directory `/run/proftpd'. Downloading https://files.pythonhosted.org/packages/9a/41/220f49aaea88bc6fa6cba8d05ecf24676326156c23b991e80b3f2fc24c77/pickleshare Copying '/home/paperless/paperless/src/documents/static/documents/img/gif.png' 5% |█▋ | 419kB 3.1MB/s eta 0:00:03 nvidia-vdpau-driver nvidia-legacy-340xx-vdpau-driver 25% |████████▎ | 696kB 5.9MB/s eta 0:00:01 Unpacking libasan4:amd64 (7.3.0-27ubuntu1~18.04) ... Regenerating fonts cache... Get:50 http://archive.ubuntu.com/ubuntu bionic/main amd64 python3-secretstorage all 2.3.1-2 [12.1 kB] Preparing to unpack .../014-imagemagick-6-common_8%3a6.9.7.4+dfsg-16ubuntu6.4_all.deb ... Preparing to unpack .../069-libtheora0_1.1.1+dfsg.1-14_amd64.deb ... * MicroK8s is Kubernetes in a snap. Made by devs for devs. 67% [4 proftpd-doc 4059 B/1165 kB 0%] Get:42 http://archive.ubuntu.com/ubuntu bionic-updates/universe amd64 libavutil55 amd64 7:3.4.4-0ubuntu0.18.04.1 [190 kB] Collecting fuzzywuzzy[speedup]==0.15.0 (from -r requirements.txt (line 27)) Preparing to unpack .../058-libpango-1.0-0_1.40.14-1ubuntu0.1_amd64.deb ... remote: Compressing objects: 100% (12/12), done. 7% [Waiting for headers] Unpacking python3-pil:amd64 (5.1.0-1) ... Downloading https://files.pythonhosted.org/packages/3e/c7/3da685ef117d42ac8d71af525208759742dd235f8094221fdaafcd3dba8f/py-1.7.0-py Preparing to unpack .../02-binutils-x86-64-linux-gnu_2.30-21ubuntu1~18.04_amd64.deb ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/et.js' Setting up libswresample2:amd64 (7:3.4.4-0ubuntu0.18.04.1) ... Get:85 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libchromaprint1 amd64 1.4.3-1 [36.8 kB] _headers-2.4.0-py2.py3-none-any.whl Unpacking libavahi-client3:amd64 (0.7-3.1ubuntu1.1) ... Unpacking libcrystalhd3:amd64 (1:0.0~git20110715.fdd2f19-12) ... Get:78 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libx264-152 amd64 2:0.152.2854+gite9a5903-2 [609 kB] Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/ro.js' 53% |█████████████████ | 491kB 5.9MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/prepopulate.js' 100% |████████████████████████████████| 2.7MB 387kB/s Unpacking libisl19:amd64 (0.19-1) ... 13% [Waiting for headers] Running setup.py bdist_wheel for regex ... | 72% |███████████████████████▍ | 5.9MB 5.9MB/s eta 0:00:01 Collecting pytz==2018.7 (from -r requirements.txt (line 65)) .0 pyocr-0.5.3 pyparsing-2.3.0 pytest-3.9.3 pytest-cov-2.6.0 pytest-django-3.4.3 pytest-env-0.6.2 pytest-forked-0.2 pytest-sugar-0.9 Found existing installation: requests 2.18.4 Preparing to unpack .../00-binutils-common_2.30-21ubuntu1~18.04_amd64.deb ... 100% |████████████████████████████████| 71kB 3.5MB/s Get:20 http://archive.ubuntu.com/ubuntu bionic/main amd64 fonts-noto-mono all 20171026-2 [75.5 kB] 16% [5 liblua5.2-0 2613 B/108 kB 2%] Site 000-default disabled. Preparing to unpack .../127-mesa-va-drivers_18.0.5-0ubuntu0~18.04.1_amd64.deb ... Get:139 http://archive.ubuntu.com/ubuntu bionic/universe amd64 i965-va-driver amd64 2.1.0-0ubuntu1 [925 kB] Setting up fakeroot (1.22-2ubuntu1) ... Selecting previously unselected package libbdplus0:amd64. Preparing to unpack .../08-libisl19_0.19-1_amd64.deb ... bms@FortBoyard:~$ ex Found existing installation: idna 2.6 One quick install on a workstation, VM, or appliance. Setting up libltdl7:amd64 (2.4.6-2) ... Receiving objects: 19% (1059/5570) 79% [43 libpython3.6-dev 33.0 MB/44.8 MB 74%] 4611 kB/s 3s Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/img/grid.png' After this operation, 271 MB of additional disk space will be used. 26% |████████▋ | 204kB 6.0MB/s eta 0:00:01 72% |███████████████████████▎ | 368kB 5.9MB/s eta 0:00:01 Get:41 http://archive.ubuntu.com/ubuntu bionic/main amd64 libexpat1-dev amd64 2.2.5-3 [122 kB] 100% |████████████████████████████████| 61kB 3.7MB/s Preparing to unpack .../07-apache2-data_2.4.29-1ubuntu4.5_all.deb ... Preparing to unpack .../015-libmagickcore-6.q16-3_8%3a6.9.7.4+dfsg-16ubuntu6.4_amd64.deb ... Unpacking libva-x11-2:amd64 (2.1.0-3) ... 8% [Waiting for headers] Setting up libasan4:amd64 (7.3.0-27ubuntu1~18.04) ... Setting up libgif7:amd64 (5.1.4-2) ... Get:28 http://archive.ubuntu.com/ubuntu bionic-updates/universe amd64 Packages [711 kB] Preparing to unpack .../123-libxcb-present0_1.13-1_amd64.deb ... 31% [10 Sources 2968 kB/9051 kB 33%] Get:44 http://archive.ubuntu.com/ubuntu bionic/main amd64 libxcb-render0 amd64 1.13-1 [14.7 kB] Preparing to unpack .../138-i965-va-driver_2.1.0-0ubuntu1_amd64.deb ... Get:93 http://archive.ubuntu.com/ubuntu bionic/main amd64 libcupsfilters1 amd64 1.20.2-0ubuntu3 [108 kB] 14% |████▊ | 112kB 6.5MB/s eta 0:00:01 Selecting previously unselected package libxcb-render0:amd64. Setting up apache2 (2.4.29-1ubuntu4.5) ... Unpacking libitm1:amd64 (8.2.0-1ubuntu2~18.04) ... Downloading https://files.pythonhosted.org/packages/b8/ad/c6f60602d3ee3d92fbed87675b6fb6a6f9a38c223343ababdb44ba201f10/Babel-2.6.0 92% [18 Packages store 0 B] [25 Sources 2687 B/122 kB 2%] 97% |███████████████████████████████▏| 1.9MB 6.0MB/s eta 0:00:01 97% [18 Packages store 0 B] logout Setting up libtsan0:amd64 (8.2.0-1ubuntu2~18.04) ... proftpd-mod-geoip Get:72 http://archive.ubuntu.com/ubuntu bionic/main amd64 libvorbis0a amd64 1.3.5-4.2 [86.4 kB] Setting up libgs9:amd64 (9.26~dfsg+0-0ubuntu0.18.04.3) ... systemctl reload apache2 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/README.txt' 40% [Working] 3755 kB/s 10s 5% |█▉ | 112kB 6.4MB/s eta 0:00:01 Setting up python3-xdg (0.25-4ubuntu1) ... Selecting previously unselected package binutils. update-alternatives: using /usr/bin/identify-im6.q16 to provide /usr/bin/identify (identify) in auto mode Selecting previously unselected package libdatrie1:amd64. liblsan0-dbg libtsan0-dbg libubsan0-dbg libcilkrts5-dbg libmpx2-dbg libquadmath0-dbg glibc-doc bzr libstdc++-7-doc make-doc Setting up libopenjp2-7:amd64 (2.3.0-1) ... Unpacking fontconfig-config (2.12.6-0ubuntu2) ... Unpacking libgdk-pixbuf2.0-common (2.36.11-2) ... 71% |██████████████████████▉ | 1.9MB 5.9MB/s eta 0:00:01 Unpacking fakeroot (1.22-2ubuntu1) ... Unpacking ssl-cert (1.0.39) ... 5.1-cp36-cp36m-manylinux1_x86_64.whl (202kB) 26% |████████▌ | 512kB 5.9MB/s eta 0:00:01 Downloading https://files.pythonhosted.org/packages/09/51/9c48a46334be50c13d25a3afe55fa05c445699304c5ad32619de953a2305/parso-0.3.1 68% |█████████████████████▉ | 4.9MB 6.0MB/s eta 0:00:01 65% |█████████████████████ | 1.3MB 6.0MB/s eta 0:00:01 utils-0.2.0-py2.py3-none-any.whl libaprutil1-ldap liblua5.2-0 ssl-cert Collecting more-itertools==4.3.0 (from -r requirements.txt (line 38)) 90% |█████████████████████████████ | 2.8MB 6.0MB/s eta 0:00:01 Selecting previously unselected package apache2. .7.12-py2.py3-none-any.whl 42% |█████████████▌ | 3.4MB 2.6MB/s eta 0:00:02 38% [Waiting for headers] 31% [Working] Unpacking libavahi-common3:amd64 (0.7-3.1ubuntu1.1) ... Enabling module filter. 32% [26 libstdc++-7-dev 4059 B/1463 kB 0%] Selecting previously unselected package libspeex1:amd64. 32% [Waiting for headers] Unpacking libilmbase12:amd64 (2.2.0-11ubuntu2) ... Get:18 http://archive.ubuntu.com/ubuntu bionic/universe amd64 Packages [8570 kB] remote: Total 5570 (delta 9), reused 21 (delta 9), pack-reused 5549 Selecting previously unselected package libtsan0:amd64. 2.0-py2.py3-none-any.whl (841kB) Unpacking libwebp6:amd64 (0.6.1-2) ... Downloading https://files.pythonhosted.org/packages/3a/e1/5f9023cc983f1a628a8c2fd051ad19e76ff7b142a0faf329336f9a62a514/attrs-18.2. Setting up fonts-noto-mono (20171026-2) ... Extracting templates from packages: 21% Not uninstalling pillow at /usr/lib/python3/dist-packages, outside environment /usr Unpacking python3.6-dev (3.6.7-1~18.04) ... Adding user paperless to group ftpupload Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/lt.js' Downloading https://files.pythonhosted.org/packages/99/0b/d37a5a96c5d301e23adcabcc2f3fa659fb34e6308590f95ebb50cdbe98a1/djangorestf Selecting previously unselected package libwebp6:amd64. Downloading https://files.pythonhosted.org/packages/f9/6e/31efb8dc1d17052c12f39262223e94038bfcc4cc7a124235630a6d50f166/pytest-env- Unpacking proftpd-basic (1.3.5e-1build1) ... Setting up libnss3:amd64 (2:3.35-2ubuntu2.1) ... Preparing to unpack .../39-libalgorithm-merge-perl_0.08-3_all.deb ... Setting up libxvidcore4:amd64 (2:1.3.5-1) ... 100% |████████████████████████████████| 122kB 3.8MB/s Preparing to unpack .../107-libmagickcore-6.q16-3-extra_8%3a6.9.7.4+dfsg-16ubuntu6.4_amd64.deb ... Unpacking libpoppler-dev:amd64 (0.62.0-2ubuntu2.5) ... 99% |████████████████████████████████| 225kB 6.1MB/s eta 0:00:01 Setting up netpbm (2:10.0-15.3build1) ... 2.6.0-py2.py3-none-any.whl Setting up libavahi-client3:amd64 (0.7-3.1ubuntu1.1) ... 0-cp36-cp36m-manylinux1_x86_64.whl (2.0MB) Collecting backcall==0.1.0 (from -r requirements.txt (line 7)) Adding new user `paperless' (1000) with group `paperless' ... 2% [4 fontconfig 2613 B/169 kB 2%] Get:77 http://archive.ubuntu.com/ubuntu bionic/main amd64 libwebpmux3 amd64 0.6.1-2 [19.6 kB] 46% [Waiting for headers] 4081 kB/s 12s Enabling module env. 6% |██ | 204kB 6.0MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/xregexp/LICENSE-XREGEXP.txt' 92% [18 Packages store 0 B] update-alternatives: using /usr/bin/stream-im6.q16 to provide /usr/bin/stream-im6 (stream-im6) in auto mode 88% |████████████████████████████▍ | 7.2MB 5.9MB/s eta 0:00:01 Creating home directory `/srv/ftp' ... Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/fonts/glyphicons-halflings-regular.eot' Setting up libcups2:amd64 (2.2.7-1ubuntu2.3) ... Collecting text-unidecode==1.2 (from -r requirements.txt (line 73)) Applying auth.0009_alter_user_last_name_max_length... OK Get:27 http://archive.ubuntu.com/ubuntu bionic/main amd64 libjbig2dec0 amd64 0.13-6 [55.9 kB] 2% [Waiting for headers] Setting up python-pip-whl (9.0.1-2.3~ubuntu1) ... Preparing to unpack .../131-python3-olefile_0.45.1-1_all.deb ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/actions.js' Running setup.py bdist_wheel for pyocr ... \ Selecting previously unselected package apache2-bin. t-1.24.0-py2.py3-none-any.whl Selecting previously unselected package libcroco3:amd64. Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/jquery/jquery.min.js' Selecting previously unselected package libpaper-utils. Preparing to unpack .../005-libjpeg-turbo8_1.5.2-0ubuntu5.18.04.1_amd64.deb ... 66% [43 libpython3.6-dev 19.2 MB/44.8 MB 43%] 4611 kB/s 6s Collecting filelock==3.0.10 (from -r requirements.txt (line 25)) Running setup.py bdist_wheel for langdetect ... / Reading package lists... Done 100% |████████████████████████████████| 122kB 3.6MB/s Selecting previously unselected package libdrm-intel1:amd64. Get:52 http://archive.ubuntu.com/ubuntu bionic/main amd64 libopus0 amd64 1.1.2-1ubuntu1 [159 kB] Preparing to unpack .../14-libatomic1_8.2.0-1ubuntu2~18.04_amd64.deb ... 13% |████▏ | 1.1MB 4.1MB/s eta 0:00:02 Get:11 http://archive.ubuntu.com/ubuntu bionic/main amd64 ssl-cert all 1.0.39 [17.0 kB] Setting up liblsan0:amd64 (8.2.0-1ubuntu2~18.04) ... Preparing to unpack .../054-libgdk-pixbuf2.0-0_2.36.11-2_amd64.deb ... Receiving objects: 5% (279/5570) Selecting previously unselected package g++-7. Unpacking libpython3.6-dev:amd64 (3.6.7-1~18.04) ... Preparing to unpack .../10-cpp-7_7.3.0-27ubuntu1~18.04_amd64.deb ... 41% |█████████████▍ | 1.3MB 6.0MB/s eta 0:00:01 Applying documents.0009_auto_20160214_0040... OK Selecting previously unselected package imagemagick-6.q16. Preparing to unpack .../libmemcachedutil2_1.0.18-4.2_amd64.deb ... Unpacking libwavpack1:amd64 (5.1.0-2ubuntu1.2) ... Downloading https://files.pythonhosted.org/packages/6e/fe/6e5b43eac75c617cd9383ce82dd76d08fa198a770b06986951b3086980c9/pytest_djan 28% |█████████▎ | 2.1MB 5.7MB/s eta 0:00:01 Get:10 http://archive.ubuntu.com/ubuntu bionic/universe Sources [9051 kB] Suggested packages: 10% |███▍ | 860kB 3.7MB/s eta 0:00:02 Get:32 http://archive.ubuntu.com/ubuntu bionic/main amd64 gsfonts all 1:8.11+urwcyr1.0.7~pre44-4.4 [3120 kB] Selecting previously unselected package libpangoft2-1.0-0:amd64. 81% [Waiting for headers] 4630 kB/s 2s 16% [Waiting for headers] 13% |████▍ | 972kB 5.8MB/s eta 0:00:02 33% [Working] Preparing to unpack .../21-libquadmath0_8.2.0-1ubuntu2~18.04_amd64.deb ... Setting up fontconfig (2.12.6-0ubuntu2) ... * Full K8s GPU support is now available! Collecting pygments==2.2.0 (from -r requirements.txt (line 51)) Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/css/vendor/select2/select2.css' 80% |█████████████████████████▋ | 614kB 5.9MB/s eta 0:00:01 Collecting pluggy==0.8.0 (from -r requirements.txt (line 45)) Receiving objects: 43% (2396/5570), 4.67 MiB | 3.10 MiB/s Setting up libmemcachedutil2:amd64 (1.0.18-4.2) ... Preparing to unpack .../021-libavahi-common3_0.7-3.1ubuntu1.1_amd64.deb ... 74% |████████████████████████ | 2.0MB 6.0MB/s eta 0:00:01 Preparing to unpack .../08-apache2_2.4.29-1ubuntu4.5_amd64.deb ... 21% |███████ | 215kB 5.2MB/s eta 0:00:01 Get:101 http://archive.ubuntu.com/ubuntu bionic/main amd64 libgdk-pixbuf2.0-bin amd64 2.36.11-2 [7864 B] Get:5 http://archive.ubuntu.com/ubuntu bionic/main amd64 liblua5.2-0 amd64 5.2.4-1.1build1 [108 kB] Unpacking apache2-bin (2.4.29-1ubuntu4.5) ... Receiving objects: 82% (4568/5570), 4.67 MiB | 3.10 MiB/s -py2.py3-none-any.whl 79% [18 Packages store 0 B] [19 Translation-en 1119 kB/4941 kB 23%] Downloading https://files.pythonhosted.org/packages/93/d6/abcb22de61d78e2fc3959c964628a5771e47e7cc60d53e9342e21ed6cc9a/traitlets-4 Selecting previously unselected package libavahi-client3:amd64. Selecting previously unselected package libvorbisfile3:amd64. Collecting atomicwrites==1.2.1 (from -r requirements.txt (line 4)) 29% [Waiting for headers] Setting up gsfonts (1:8.11+urwcyr1.0.7~pre44-4.4) ... go-3.4.3-py2.py3-none-any.whl Running setup.py bdist_wheel for langdetect ... - Selecting previously unselected package gcc. (Reading database ... 32033 files and directories currently installed.) Setting up libdpkg-perl (1.19.0.5ubuntu2.1) ... Applying documents.0005_auto_20160123_0313... OK 99% [29 Translation-en store 0 B] 4229 kB/s 0s update-alternatives: using /usr/bin/mogrify-im6.q16 to provide /usr/bin/mogrify (mogrify) in auto mode Setting up libxcb-dri2-0:amd64 (1.13-1) ... Setting up libavahi-common3:amd64 (0.7-3.1ubuntu1.1) ... Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/docs/img/grid.png' Setting up libx265-146:amd64 (2.6-3) ... Downloading https://files.pythonhosted.org/packages/62/94/5430ebaa83f91cc7a9f687ff5238e26164a779cca2ef9903232268b0a318/Pillow-5.3. bms@FortBoyard:~$ exit 34% |███████████ | 174kB 5.9MB/s eta 0:00:01 Get:12 http://archive.ubuntu.com/ubuntu bionic/main amd64 liblqr-1-0 amd64 0.4.2-2.1 [27.7 kB] Preparing to unpack .../061-libpangoft2-1.0-0_1.40.14-1ubuntu0.1_amd64.deb ... Get:66 http://archive.ubuntu.com/ubuntu bionic/main amd64 libsnappy1v5 amd64 1.1.7-1 [16.0 kB] libmemcached11 libmemcachedutil2 proftpd-doc Unpacking python3-venv (3.6.7-1~18.04) ... Applying documents.0008_document_file_type... OK Unpacking libdrm-radeon1:amd64 (2.4.91-2) ... 98% [138 unpaper 2613 B/242 kB 1%] 4630 kB/s 0s Get:4 http://archive.ubuntu.com/ubuntu bionic-backports InRelease [74.6 kB] Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/zh-CN.js' Preparing to unpack .../09-libmpc3_1.1.0-1_amd64.deb ... 44% [34 python3-distutils 2613 B/141 kB 2%] 4081 kB/s 12s Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.16.133|:443... connected. Processing triggers for libc-bin (2.27-3ubuntu1) ... Preparing to unpack .../24-gcc_4%3a7.3.0-3ubuntu2.1_amd64.deb ... Get:14 http://archive.ubuntu.com/ubuntu bionic/main amd64 libtiff5 amd64 4.0.9-5 [152 kB] Selecting previously unselected package libtesseract4. update-alternatives: using /usr/bin/montage-im6.q16 to provide /usr/bin/montage (montage) in auto mode Preparing to unpack .../090-libavformat57_7%3a3.4.4-0ubuntu0.18.04.1_amd64.deb ... Get:86 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libgme0 amd64 0.6.2-1 [121 kB] Unpacking mesa-vdpau-drivers:amd64 (18.0.5-0ubuntu0~18.04.1) ... 21% [Waiting for headers] Selecting previously unselected package python3.6-venv. Selecting previously unselected package libaprutil1-ldap:amd64. Downloading https://files.pythonhosted.org/packages/d1/29/605c2cc68a9992d18dada28206eeada56ea4bd07a239669da41674648b6f/ptyprocess- 10% |███▎ | 204kB 5.9MB/s eta 0:00:01 98% |███████████████████████████████▍| 174kB 6.0MB/s eta 0:00:01 Preparing to unpack .../136-tesseract-ocr_4.00~git2288-10f4998a-2_amd64.deb ... 33% [Waiting for headers] Preparing to unpack .../05-linux-libc-dev_4.15.0-43.46_amd64.deb ... 56% |██████████████████ | 4.5MB 5.9MB/s eta 0:00:01 Extracting templates from packages: 100% Setting up libapr1:amd64 (1.6.3-2) ... Successfully installed alabaster-0.7.12 apipkg-1.5 atomicwrites-1.2.1 attrs-18.2.0 babel-2.6.0 backcall-0.1.0 certifi-2018.10.15 cov Downloading https://files.pythonhosted.org/packages/30/0a/1b009b525526cd3cd9f52f52391b426c5a3597447be811a10bcb1f6b05eb/pytest_cov- Preparing to unpack .../008-libjbig0_2.1-3.1build1_amd64.deb ... 17% [Waiting for headers] Get:8 http://archive.ubuntu.com/ubuntu bionic/multiverse Sources [181 kB] Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/fonts/fontawesome-webfont.svg' Preparing to unpack .../044-libxcb-shm0_1.13-1_amd64.deb ... Welcome to Ubuntu 18.04.1 LTS (GNU/Linux 4.15.0-43-generic x86_64) - https://bit.ly/microk8s .tar.gz Downloading https://files.pythonhosted.org/packages/8c/da/b8dd8deb741bff556db53902d4706774c8e1e67265f69528c14c003644e6/gunicorn-19 Get:52 http://archive.ubuntu.com/ubuntu bionic/main amd64 python3-keyrings.alt all 3.0-1 [16.6 kB] Get:79 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libx265-146 amd64 2.6-3 [1026 kB] Preparing to unpack .../46-python3-crypto_2.6.1-8ubuntu2_amd64.deb ... Preparing to unpack .../092-libcupsfilters1_1.20.2-0ubuntu3_amd64.deb ... 2% [5 fonts-droid-fallback 2611 B/1805 kB 0%] Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/xregexp/xregexp.min.js' 49% |████████████████ | 4.0MB 6.0MB/s eta 0:00:01 Copying files from `/etc/skel' ... Selecting previously unselected package libpangocairo-1.0-0:amd64. update-alternatives: using /usr/bin/g++ to provide /usr/bin/c++ (c++) in auto mode Unpacking libbinutils:amd64 (2.30-21ubuntu1~18.04) ... Setting up python3-pyocr (0.3.0-1) ... 58% |██████████████████▉ | 1.8MB 5.9MB/s eta 0:00:01 Get:13 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libcc1-0 amd64 8.2.0-1ubuntu2~18.04 [39.5 kB] Preparing to unpack .../115-libpoppler-cpp-dev_0.62.0-2ubuntu2.5_amd64.deb ... 9% |███▏ | 92kB 5.8MB/s eta 0:00:01 Unpacking libzvbi-common (0.2.35-13) ... Selecting previously unselected package libva-drm2:amd64. Unpacking fonts-noto-mono (20171026-2) ... Collecting execnet==1.5.0 (from -r requirements.txt (line 22)) 13% [11 cpp-7 2873 kB/6738 kB 43%] 87% |████████████████████████████ | 7.1MB 5.8MB/s eta 0:00:01 Downloading https://files.pythonhosted.org/packages/79/42/d717cc2b4520fb09e45b344b1b0b4e81aa672001dd128c180fabc655c341/text_unidec 5% [6 libjpeg-turbo8 2613 B/110 kB 2%] libcupsfilters1 libcupsimage2 libdatrie1 libdjvulibre-text libdjvulibre21 libdrm-amdgpu1 libdrm-intel1 libdrm-nouveau2 37% [Waiting for headers] Preparing to unpack .../080-libzvbi-common_0.2.35-13_all.deb ... 93% [18 Packages store 0 B] Get:6 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libjpeg-turbo8 amd64 1.5.2-0ubuntu5.18.04.1 [110 kB] Selecting previously unselected package libbluray2:amd64. Selecting previously unselected package librsvg2-2:amd64. 8% [Working] 97% [Working] 4944 kB/s 0s Selecting previously unselected package libapr1:amd64. Selecting previously unselected package python3-pip. py2.py3-none-any.whl Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/eu.js' Get:105 http://archive.ubuntu.com/ubuntu bionic/main amd64 libllvm6.0 amd64 1:6.0-1ubuntu2 [14.5 MB] Selecting previously unselected package liblqr-1-0:amd64. Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/css/responsive_rtl.css' 55% |█████████████████▊ | 4.0MB 6.0MB/s eta 0:00:01 Preparing to unpack .../096-libpciaccess0_0.14-1_amd64.deb ... Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/img/glyphicons-halflings-white.png' 44% |██████████████▍ | 3.6MB 5.4MB/s eta 0:00:01 libdrm-radeon1 libfftw3-double3 libfontconfig1 libgdk-pixbuf2.0-0 libgdk-pixbuf2.0-bin libgdk-pixbuf2.0-common libgif7 libgme0 Get:103 http://archive.ubuntu.com/ubuntu bionic/main amd64 libilmbase12 amd64 2.2.0-11ubuntu2 [71.4 kB] 50% [84 libbluray2 2613 B/141 kB 2%] 3755 kB/s 9s Get:10 http://archive.ubuntu.com/ubuntu bionic/main amd64 libjpeg8 amd64 8c-2ubuntu8 [2194 B] Processes: 393 update-alternatives: using /usr/bin/conjure-im6.q16 to provide /usr/bin/conjure (conjure) in auto mode Setting up libgme0:amd64 (0.6.2-1) ... Get:34 http://archive.ubuntu.com/ubuntu bionic-backports/universe Translation-en [1604 B] Preparing to unpack .../112-libpoppler73_0.62.0-2ubuntu2.5_amd64.deb ... Collecting regex==2018.11.2 (from -r requirements.txt (line 66)) Processing triggers for man-db (2.8.3-2ubuntu0.1) ... Enabling module authz_host. Applying documents.0003_sender... OK 99% [28 Packages store 0 B] 4229 kB/s 0s Running migrations: Get:80 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libxvidcore4 amd64 2:1.3.5-1 [200 kB] Preparing to unpack .../125-libxcb-xfixes0_1.13-1_amd64.deb ... Unpacking libaprutil1:amd64 (1.6.1-2) ... 77% [107 libwmf0.2-7 2613 B/150 kB 2%] 3755 kB/s 3s Selecting previously unselected package tesseract-ocr. Unpacking libogg0:amd64 (1.3.2-1) ... Get:46 http://archive.ubuntu.com/ubuntu bionic-updates/universe amd64 python-pip-whl all 9.0.1-2.3~ubuntu1 [1652 kB] Unpacking libijs-0.35:amd64 (0.35-13) ... Collecting python-dotenv==0.9.1 (from -r requirements.txt (line 62)) automake libtool flex bison gdb gcc-doc gcc-7-multilib libgcc1-dbg libgomp1-dbg libitm1-dbg libatomic1-dbg libasan4-dbg t, python-dotenv, python-gnupg, snowballstemmer, sphinxcontrib-websupport, sphinx, toml, virtualenv, tox 46% [Working] 4081 kB/s 12s Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/collapse.js' 58% [105 libllvm6.0 1162 B/14.5 MB 0%] 3755 kB/s 7s Preparing to unpack .../37-libalgorithm-diff-perl_1.19.03-1_all.deb ... Get:18 http://archive.ubuntu.com/ubuntu bionic/main amd64 libogg0 amd64 1.3.2-1 [17.2 kB] update-alternatives: using /usr/bin/compare-im6.q16 to provide /usr/bin/compare-im6 (compare-im6) in auto mode Setting up libzvbi0:amd64 (0.2.35-13) ... Setting up imagemagick (8:6.9.7.4+dfsg-16ubuntu6.4) ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/gis/move_vertex_on.svg' Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/jquery/LICENSE-JQUERY.txt' Get:3 http://archive.ubuntu.com/ubuntu bionic/main amd64 libaprutil1-dbd-sqlite3 amd64 1.6.1-2 [10.6 kB] 90% [Waiting for headers] 4630 kB/s 1s Unpacking libubsan0:amd64 (7.3.0-27ubuntu1~18.04) ... 76% |████████████████████████▌ | 1.5MB 12.4MB/s eta 0:00:01 Unpacking va-driver-all:amd64 (2.1.0-3) ... -py2.py3-none-any.whl (766kB) Unpacking g++-7 (7.3.0-27ubuntu1~18.04) ... al python-levenshtein Downloading https://files.pythonhosted.org/packages/89/d1/92e6df2e503a69df9faab187c684585f0136662c12bb1f36901d426f3fab/packaging-1 Downloading https://files.pythonhosted.org/packages/e5/b4/c6102d9dea6a4cbc5a56ebe5843c3123e5013c17a4f2f0f6d490d821766e/regex-2018. Successfully built backcall docopt filemagic inotify-simple langdetect pdftotext pyocr pytest-env pytest-sugar regex termcolor tzloc Unpacking gcc-7 (7.3.0-27ubuntu1~18.04) ... 84% |███████████████████████████▏ | 6.0MB 5.8MB/s eta 0:00:01 31% |██████████▏ | 2.6MB 4.0MB/s eta 0:00:02 92% [Working] 4630 kB/s 1s 43% [Waiting for headers] 4081 kB/s 12s Enabling module autoindex. 48% |███████████████▋ | 3.5MB 6.0MB/s eta 0:00:01 95% [48 python3.6-dev 2613 B/508 kB 1%] 4944 kB/s 0s 98% |███████████████████████████████▋| 757kB 6.2MB/s eta 0:00:01 84% [Working] 4630 kB/s 2s 44% [Working] 4081 kB/s 12s Preparing to unpack .../024-libcupsimage2_2.2.7-1ubuntu2.3_amd64.deb ... Unpacking liblua5.2-0:amd64 (5.2.4-1.1build1) ... 1% [3 binutils-x86-64-linux-gnu 2611 B/1855 kB 0%] Unpacking gcc (4:7.3.0-3ubuntu2.1) ... Copying '/home/paperless/paperless/src/documents/static/js/colours.js' 47% [41 libexpat1-dev 4061 B/122 kB 3%] 4081 kB/s 12s Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/ar.js' Preparing to unpack .../033-imagemagick-6.q16_8%3a6.9.7.4+dfsg-16ubuntu6.4_amd64.deb ... Email address: Selecting previously unselected package libalgorithm-diff-xs-perl. 99% |███████████████████████████████▉| 1.9MB 5.9MB/s eta 0:00:01 Selecting previously unselected package libtheora0:amd64. 100% |████████████████████████████████| 645kB 1.3MB/s Downloading https://files.pythonhosted.org/packages/7e/9f/526a6947247599b084ee5232e4f9190a38f398d7300d866af3ab571a5bfe/wcwidth-0.1 Setting up libcroco3:amd64 (0.6.12-2) ... Get:54 http://archive.ubuntu.com/ubuntu bionic/main amd64 python3-setuptools all 39.0.1-2 [248 kB] Running setup.py bdist_wheel for backcall ... done Selecting previously unselected package libcupsfilters1:amd64. www-browser apache2-doc apache2-suexec-pristine | apache2-suexec-custom openssl-blacklist .0 tox-3.5.3 traitlets-4.3.2 tzlocal-1.5.1 urllib3-1.24.1 virtualenv-16.1.0 wcwidth-0.1.7 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/uk.js' Selecting previously unselected package libxcb-sync1:amd64. update-alternatives: using /usr/bin/import-im6.q16 to provide /usr/bin/import-im6 (import-im6) in auto mode Selecting previously unselected package binutils-x86-64-linux-gnu. 97% [18 Packages store 0 B] Found existing installation: urllib3 1.22 8.10.15-py2.py3-none-any.whl (146kB) Preparing to unpack .../067-libsoxr0_0.1.2-3_amd64.deb ... Preparing to unpack .../01-libbinutils_2.30-21ubuntu1~18.04_amd64.deb ... 69% |██████████████████████▏ | 5.6MB 5.9MB/s eta 0:00:01 Preparing to unpack .../027-libpaper1_1.1.24+nmu5ubuntu1_amd64.deb ... Downloading https://files.pythonhosted.org/packages/42/a9/d1785c85ebf9b7dfacd08938dd028209c34a0ea3b1bcdb895208bd40a67d/python-Leve Swap usage: 0% IP address for virbr0: 192.168.122.1 Preparing to unpack .../088-libopenmpt0_0.3.6-1_amd64.deb ... Collecting pdftotext==2.1.1 (from -r requirements.txt (line 41)) 17% [Waiting for headers] Downloading https://files.pythonhosted.org/packages/7a/1a/9bd24a185873b998611c2d8d4fb15cd5e8a879ead36355df7ee53e9111bf/jedi-0.13.1 Stored in directory: /root/.cache/pip/wheels/ae/1c/56/c27dc42b329eaf83222ba4f14c084489427e591a385b2209dc 21% [Working] Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/js/jquery-3.3.1.min.js' 74% [43 libpython3.6-dev 27.7 MB/44.8 MB 62%] 4611 kB/s 4s Downloading https://files.pythonhosted.org/packages/cc/7e/83ba784ad2b95317bbbed915f0888d7d1cd8dc3d2e4b8ddec8fbc4c3e800/django_cors Selecting previously unselected package libdrm-amdgpu1:amd64. Enabling module auth_basic. 167 static files copied to '/home/paperless/paperless/static'. libsoxr0 libspeex1 libssh-gcrypt-4 libswresample2 libtesseract4 libthai-data libthai0 libtheora0 libtiff5 libtwolame0 libva-drm2 66% |█████████████████████▍ | 4.8MB 5.2MB/s eta 0:00:01 Get:3 http://archive.ubuntu.com/ubuntu bionic/universe amd64 proftpd-basic amd64 1.3.5e-1build1 [2015 kB] Unpacking libmagickwand-6.q16-3:amd64 (8:6.9.7.4+dfsg-16ubuntu6.4) ... Setting up libllvm6.0:amd64 (1:6.0-1ubuntu2) ... Get:1 http://archive.ubuntu.com/ubuntu bionic/main amd64 libmemcached11 amd64 1.0.18-4.2 [83.3 kB] Applying admin.0002_logentry_remove_auto_add... OK Preparing to unpack .../libmemcached11_1.0.18-4.2_amd64.deb ... Setting up libwebpdemux2:amd64 (0.6.1-2) ... 2.py3-none-any.whl (58kB) Get:75 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libwavpack1 amd64 5.1.0-2ubuntu1.2 [76.5 kB] 73% [10 Sources store 0 B] [18 Packages 7883 kB/8570 kB 92%] 35% |███████████▍ | 706kB 5.2MB/s eta 0:00:01 Preparing to unpack .../45-python-pip-whl_9.0.1-2.3~ubuntu1_all.deb ... Selecting previously unselected package imagemagick-6-common. 90% [18 Packages store 0 B] Setting up libmp3lame0:amd64 (3.100-2) ... 94% [18 Packages store 0 B] Get:19 http://archive.ubuntu.com/ubuntu bionic/universe Translation-en [4941 kB] Preparing to unpack .../128-mesa-vdpau-drivers_18.0.5-0ubuntu0~18.04.1_amd64.deb ... 72% |███████████████████████▎ | 727kB 6.0MB/s eta 0:00:01 Setting up proftpd-basic (1.3.5e-1build1) ... Receiving objects: 29% (1616/5570), 2.15 MiB | 2.14 MiB/s Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/js/prettify-min.js' 63% |████████████████████▍ | 5.2MB 6.0MB/s eta 0:00:01 er-2.0.0-py3-none-any.whl (69kB) Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/cancel.js' Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/sv.js' Selecting previously unselected package gcc-7. 90% [45 manpages-dev 4059 B/2217 kB 0%] 4611 kB/s 1s 89% [43 libpython3.6-dev 43.9 MB/44.8 MB 98%] 4611 kB/s 1s 100% |████████████████████████████████| 512kB 1.7MB/s 99% |████████████████████████████████| 542kB 6.7MB/s eta 0:00:01 Copying '/home/paperless/paperless/src/documents/static/documents/img/png.png' Running setup.py bdist_wheel for inotify-simple ... - 82% |██████████████████████████▎ | 2.2MB 5.9MB/s eta 0:00:01 14% |████▊ | 112kB 6.4MB/s eta 0:00:01 r-0.9.1.tar.gz 14% [29 libgs9-common 2611 B/5094 kB 0%] libxcb-xfixes0 libxfixes3 libxrender1 libxshmfence1 libxvidcore4 libzvbi-common libzvbi0 mesa-va-drivers mesa-vdpau-drivers update-alternatives: using /usr/bin/display-im6.q16 to provide /usr/bin/display (display) in auto mode Running setup.py bdist_wheel for langdetect ... | Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/css/default.css' Unpacking libxfixes3:amd64 (1:5.0.3-1) ... 91% [Waiting for headers] 4630 kB/s 1s 75% |████████████████████████▏ | 409kB 5.2MB/s eta 0:00:01 15% [11 cpp-7 5431 kB/6738 kB 81%] Preparing to unpack .../081-libzvbi0_0.2.35-13_amd64.deb ... 100% |████████████████████████████████| 71kB 3.6MB/s Selecting previously unselected package libzvbi0:amd64. Selecting previously unselected package libcairo2:amd64. Unpacking libthai-data (0.1.27-2) ... 72% [105 libllvm6.0 11.5 MB/14.5 MB 79%] 3755 kB/s 4s 18% |█████▉ | 153kB 5.2MB/s eta 0:00:01 94% [18 Packages store 0 B] [28 Packages 1595 B/711 kB 0%] Preparing to unpack .../137-unpaper_6.1-2_amd64.deb ... Last login: Sun Jan 20 19:46:40 2019 from 192.168.1.136 .6.tar.gz Setting up libubsan0:amd64 (7.3.0-27ubuntu1~18.04) ... Get:48 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libcrystalhd3 amd64 1:0.0~git20110715.fdd2f19-12 [45.8 kB] 93% |██████████████████████████████ | 2.9MB 5.7MB/s eta 0:00:01 -py2.py3-none-any.whl (88kB) 0% [1 libapr1 2614 B/90.9 kB 3%] 60% |███████████████████▎ | 215kB 5.2MB/s eta 0:00:01 Collecting babel==2.6.0 (from -r requirements.txt (line 6)) Get:9 http://archive.ubuntu.com/ubuntu bionic/main amd64 libisl19 amd64 0.19-1 [551 kB] | fonts-ipafont-gothic fonts-arphic-ukai fonts-arphic-uming fonts-nanum python-pil-doc python3-pil-dbg libvdpau-va-gl1 Setting up cpp (4:7.3.0-3ubuntu2.1) ... Get:51 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libopenjp2-7 amd64 2.3.0-1 [145 kB] Selecting previously unselected package libmemcachedutil2:amd64. Running setup.py bdist_wheel for regex ... / Get:119 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libtesseract4 amd64 4.00~git2288-10f4998a-2 [1163 kB] 79% [Waiting for headers] 4630 kB/s 2s Selecting previously unselected package libaprutil1-dbd-sqlite3:amd64. 85% |███████████████████████████▌ | 307kB 5.9MB/s eta 0:00:01 Selecting previously unselected package libavahi-common-data:amd64. Get:44 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libpython3-dev amd64 3.6.7-1~18.04 [7328 B] Selecting previously unselected package libjbig0:amd64. 27% |████████▊ | 2.2MB 5.2MB/s eta 0:00:02 Selecting previously unselected package libxcb-dri2-0:amd64. Applying reminders.0002_auto_20181007_1420... OK 36% [Working] Get:30 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libgs9 amd64 9.26~dfsg+0-0ubuntu0.18.04.3 [2263 kB] Unpacking libdjvulibre21:amd64 (3.5.27.1-8) ... 34% [Waiting for headers] 65% |█████████████████████ | 5.3MB 5.9MB/s eta 0:00:01 libmagickwand-6.q16-3 libmp3lame0 libmpg123-0 libnetpbm10 libnspr4 libnss3 libogg0 libopenexr22 libopenjp2-7 libopenmpt0 Selecting previously unselected package python3-pil:amd64. 22% |███████▎ | 706kB 5.7MB/s eta 0:00:01 25% [12 Translation-en store 0 B] [10 Sources 1717 kB/9051 kB 19%] [Waiting for headers] Running setup.py bdist_wheel for pdftotext ... done Unpacking libdrm-intel1:amd64 (2.4.91-2) ... Preparing to unpack .../06-libc6-dev_2.27-3ubuntu1_amd64.deb ... Enabling module setenvif. 91% [18 Packages store 0 B] 56% [102 libgif7 2615 B/30.6 kB 9%] 3755 kB/s 8s Unpacking libwebpmux3:amd64 (0.6.1-2) ... Receiving objects: 27% (1504/5570), 2.15 MiB | 2.14 MiB/s Not uninstalling idna at /usr/lib/python3/dist-packages, outside environment /usr Unpacking liblept5 (1.75.3-3) ... Stored in directory: /root/.cache/pip/wheels/97/3e/62/04d426143d73ee23cb6bc1cabc52563c452889a43f71d32338 Unpacking build-essential (12.4ubuntu1) ... Get:49 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libgsm1 amd64 1.0.13-4build1 [22.4 kB] 31% [24 gcc-7 7325 kB/7455 kB 98%] 13% [Waiting for headers] Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.16.133|:443... Running setup.py bdist_wheel for python-levenshtein ... | 97% [18 Packages store 0 B] 99% [Waiting for headers] 4944 kB/s 0s 22% |███████▏ | 604kB 6.0MB/s eta 0:00:01 0% [Working] Receiving objects: 24% (1337/5570), 2.15 MiB | 2.14 MiB/s 33% [Waiting for headers] 4081 kB/s 14s 89% |████████████████████████████▌ | 6.3MB 6.0MB/s eta 0:00:01 Setting up libgdk-pixbuf2.0-bin (2.36.11-2) ... 86% |███████████████████████████▋ | 6.1MB 6.0MB/s eta 0:00:01 Selecting previously unselected package libsoxr0:amd64. Preparing to unpack .../000-fonts-dejavu-core_2.37-1_all.deb ... Collecting pycodestyle==2.4.0 (from -r requirements.txt (line 50)) Preparing to unpack .../26-g++-7_7.3.0-27ubuntu1~18.04_amd64.deb ... 31% |██████████▎ | 614kB 6.0MB/s eta 0:00:01 Setting up libdjvulibre-text (3.5.27.1-8) ... Processing triggers for systemd (237-3ubuntu10.11) ... Get:49 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 python3-dev amd64 3.6.7-1~18.04 [1288 B] Selecting previously unselected package python3-keyring. Downloading https://files.pythonhosted.org/packages/a2/12/ced7105d2de62fa7c8fb5fce92cc4ce66b57c95fb875e9318dba7f8c5db0/toml-0.10.0 Preparing to unpack .../073-libvpx5_1.7.0-3_amd64.deb ... Building wheels for collected packages: backcall, docopt, filemagic, inotify-simple, langdetect, pdftotext, pyocr, pytest-env, pytes 7% [Waiting for headers] Unpacking libssh-gcrypt-4:amd64 (0.8.0~20170825.94fa1e38-1ubuntu0.2) ... Unpacking gsfonts (1:8.11+urwcyr1.0.7~pre44-4.4) ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/tr.js' Need to get 64.4 MB of archives. Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/calendar-icons.svg' Copying '/home/paperless/paperless/src/paperless/static/paperless/img/favicon.ico' Enabling module authn_file. 38% |████████████▍ | 2.8MB 5.9MB/s eta 0:00:01 Preparing to unpack .../15-libasan4_7.3.0-27ubuntu1~18.04_amd64.deb ... Get:27 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 g++-7 amd64 7.3.0-27ubuntu1~18.04 [7570 kB] 33% |██████████▊ | 901kB 5.2MB/s eta 0:00:01 Collecting py==1.7.0 (from -r requirements.txt (line 49)) 20% [Waiting for headers] 99% |████████████████████████████████| 921kB 6.0MB/s eta 0:00:01 Selecting previously unselected package proftpd-basic. Selecting previously unselected package imagemagick. 4% |█▎ | 286kB 5.9MB/s eta 0:00:02 Downloading https://files.pythonhosted.org/packages/62/00/ee1d7de624db8ba7090d1226aebefab96a2c71cd5cfa7629d6ad3f61b79e/urllib3-1.2 Downloading https://files.pythonhosted.org/packages/ac/9e/1aa87c0c59f9731820bfd20a8b148d97b315530c2c92d1fb300328c8c42f/dateparser- Selecting previously unselected package make. Collecting pexpect==4.6.0 (from -r requirements.txt (line 42)) 16% |█████▎ | 512kB 5.8MB/s eta 0:00:01 31% |██████████▏ | 2.3MB 5.9MB/s eta 0:00:01 Get:32 http://archive.ubuntu.com/ubuntu bionic/main amd64 build-essential amd64 12.4ubuntu1 [4758 B] 42% [30 libdpkg-perl 2613 B/211 kB 1%] 4081 kB/s 12s 9.2 filelock-3.0.10 filemagic-1.6 fuzzywuzzy-0.15.0 gunicorn-19.9.0 idna-2.7 imagesize-1.1.0 inotify-simple-1.1.8 ipython-7.1.1 ipyt 2.py3-none-any.whl (83kB) Selecting previously unselected package libatomic1:amd64. Preparing to unpack .../087-libvorbisfile3_1.3.5-4.2_amd64.deb ... Selecting previously unselected package optipng. Downloading https://files.pythonhosted.org/packages/f1/ca/10332a30cb25b627192b4ea272c351bce3ca1091e541245cccbace6051d8/requests-2. Collecting traitlets==4.3.2 (from -r requirements.txt (line 76)) 34% [56 libthai-data 2613 B/133 kB 2%] Installing collected packages: alabaster, apipkg, atomicwrites, attrs, pytz, babel, backcall, certifi, coverage, idna, urllib3, requ Get:55 http://archive.ubuntu.com/ubuntu bionic/main amd64 libgdk-pixbuf2.0-0 amd64 2.36.11-2 [165 kB] Unpacking libthai0:amd64 (0.1.27-2) ... Preparing to unpack .../133-python3-pyocr_0.3.0-1_all.deb ... Fetched 85.2 MB in 19s (4415 kB/s) libpoppler-cpp-dev libpoppler-cpp0v5 libpoppler-dev libpoppler73 librsvg2-2 librsvg2-common libsensors4 libshine3 libsnappy1v5 Get:23 http://archive.ubuntu.com/ubuntu bionic-updates/multiverse Sources [3820 B] Preparing to unpack .../099-libdrm-radeon1_2.4.91-2_amd64.deb ... Preparing to unpack .../30-dpkg-dev_1.19.0.5ubuntu2.1_all.deb ... Unpacking libalgorithm-merge-perl (0.08-3) ... 0% [1 InRelease gpgv 242 kB] [3 InRelease 28.7 kB/88.7 kB 32%] 41% |█████████████▎ | 317kB 5.9MB/s eta 0:00:01 Selecting previously unselected package libnss3:amd64. Selecting previously unselected package ghostscript. Unpacking libspeex1:amd64 (1.2~rc1.2-1ubuntu2) ... Get:5 http://archive.ubuntu.com/ubuntu bionic/main Sources [829 kB] Copying '/home/paperless/paperless/src/documents/static/documents/img/pdf.png' Get:132 http://archive.ubuntu.com/ubuntu bionic/main amd64 python3-olefile all 0.45.1-1 [33.3 kB] 97% [22 Sources store 0 B] 4229 kB/s 0s Selecting previously unselected package libmemcached11:amd64. Preparing to unpack .../124-libxcb-sync1_1.13-1_amd64.deb ... libssh-gcrypt-4 libswresample2 libtesseract4 libthai-data libthai0 libtheora0 libtiff5 libtwolame0 libva-drm2 libva-x11-2 libva2 0 upgraded, 4 newly installed, 0 to remove and 19 not upgraded. .7-py2.py3-none-any.whl 0% |▏ | 30kB 6.7MB/s eta 0:00:02 90% [Working] 4611 kB/s 1s Downloading https://files.pythonhosted.org/packages/9a/05/6bad05742d185ec2fabfa4deab05cafde286eb3f383fba24b3674340aca2/django_cris Preparing to unpack .../056-libdatrie1_0.2.10-7_amd64.deb ... Get:25 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libcupsimage2 amd64 2.2.7-1ubuntu2.3 [18.6 kB] Preparing to unpack .../070-libtwolame0_0.3.13-3_amd64.deb ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/icon-no.svg' Unpacking libcupsimage2:amd64 (2.2.7-1ubuntu2.3) ... 10% [11 cpp-7 4059 B/6738 kB 0%] Selecting previously unselected package libwebpmux3:amd64. Preparing to unpack .../10-ssl-cert_1.0.39_all.deb ... 7% |██▌ | 204kB 5.8MB/s eta 0:00:01 libjpeg8 liblcms2-2 liblept5 libllvm6.0 liblqr-1-0 libltdl7 libmagickcore-6.q16-3 libmagickcore-6.q16-3-extra 46% |██████████████▊ | 3.7MB 5.9MB/s eta 0:00:01 Unpacking libjbig0:amd64 (2.1-3.1build1) ... Selecting previously unselected package libogg0:amd64. rvice. Selecting previously unselected package libxshmfence1:amd64. Running setup.py bdist_wheel for filemagic ... - Hit:1 http://archive.ubuntu.com/ubuntu bionic InRelease Password (again): Get:29 http://archive.ubuntu.com/ubuntu bionic/main amd64 make amd64 4.1-9.1ubuntu1 [154 kB] Get:25 http://archive.ubuntu.com/ubuntu bionic-updates/universe Sources [122 kB] 100% [Waiting for headers] 4630 kB/s 0s Enabling module reqtimeout. 100% [Waiting for headers] 4944 kB/s 0s 83% |██████████████████████████▊ | 5.9MB 6.0MB/s eta 0:00:01 Selecting previously unselected package libfile-fcntllock-perl. Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/js/ajax-form.js' 97% |███████████████████████████████▎| 81kB 5.4MB/s eta 0:00:01 Selecting previously unselected package libtwolame0:amd64. Collecting factory-boy==2.11.1 (from -r requirements.txt (line 23)) 26% [10 Sources 1908 kB/9051 kB 21%] [13 Packages 2687 B/113 kB 2%] Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/css/rtl.css' Preparing to unpack .../012-libltdl7_2.4.6-2_amd64.deb ... 98% [27 Translation-en store 0 B] 4229 kB/s 0s Setting up libx264-152:amd64 (2:0.152.2854+gite9a5903-2) ... 5% [Working] 97% |███████████████████████████████▎| 3.0MB 6.0MB/s eta 0:00:01 10% [17 libmagickwand-6.q16-3 1165 B/293 kB 0%] Preparing to unpack .../032-hicolor-icon-theme_0.17-2_all.deb ... Get:15 http://security.ubuntu.com/ubuntu bionic-security/multiverse amd64 Packages [1444 B] Selecting previously unselected package libstdc++-7-dev:amd64. 14% [Waiting for headers] Unpacking libfakeroot:amd64 (1.22-2ubuntu1) ... Setting up libpixman-1-0:amd64 (0.34.0-2) ... 95% |██████████████████████████████▌ | 112kB 6.4MB/s eta 0:00:01 Collecting pytest-sugar==0.9.1 (from -r requirements.txt (line 58)) Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/de.js' Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/core.js' Get:31 http://archive.ubuntu.com/ubuntu bionic-updates/multiverse Translation-en [3356 B] fontconfig fontconfig-config fonts-dejavu-core fonts-droid-fallback fonts-noto-mono ghostscript gsfonts hicolor-icon-theme Unpacking cpp-7 (7.3.0-27ubuntu1~18.04) ... Get:109 http://archive.ubuntu.com/ubuntu bionic/main amd64 libnetpbm10 amd64 2:10.0-15.3build1 [58.0 kB] 81% [Working] 4630 kB/s 2s 37% [Waiting for headers] 28% [10 Sources 2366 kB/9051 kB 26%] [14 Translation-en 2689 B/64.2 kB 4%] 26% |████████▍ | 1.9MB 5.8MB/s eta 0:00:01 ests, docopt, coveralls, tzlocal, regex, python-dateutil, dateparser, decorator, django-cors-headers, django-crispy-forms, django-ex Get:25 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 gcc amd64 4:7.3.0-3ubuntu2.1 [5184 B] 99% |████████████████████████████████| 2.7MB 5.6MB/s eta 0:00:01 0% [5 Sources store 0 B] [3 InRelease gpgv 88.7 kB] [Waiting for headers] 3% |█▏ | 286kB 1.6MB/s eta 0:00:05 Setting up libapache2-mod-wsgi-py3 (4.5.17-1) ... Selecting previously unselected package libva2:amd64. Get:43 http://archive.ubuntu.com/ubuntu bionic/main amd64 libpixman-1-0 amd64 0.34.0-2 [229 kB] Setting up libalgorithm-merge-perl (0.08-3) ... Preparing to unpack .../031-gsfonts_1%3a8.11+urwcyr1.0.7~pre44-4.4_all.deb ... Selecting previously unselected package binutils-common:amd64. Unpacking libllvm6.0:amd64 (1:6.0-1ubuntu2) ... Get:17 http://archive.ubuntu.com/ubuntu bionic/restricted Sources [5324 B] Setting up libcrystalhd3:amd64 (1:0.0~git20110715.fdd2f19-12) ... Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/css/font-awesome-4.0.3.css' 8.0-py2.py3-none-any.whl 56% |██████████████████▎ | 1.1MB 33.1MB/s eta 0:00:01 Unpacking librsvg2-common:amd64 (2.40.20-2) ... Setting up linux-libc-dev:amd64 (4.15.0-43.46) ... Setting up libalgorithm-diff-perl (1.19.03-1) ... Unpacking python3-lib2to3 (3.6.7-1~18.04) ... Retype new UNIX password: 11.02.tar.gz (644kB) 70% |██████████████████████▋ | 5.7MB 5.8MB/s eta 0:00:01 Setting up libmagickcore-6.q16-3-extra:amd64 (8:6.9.7.4+dfsg-16ubuntu6.4) ... 42% |█████████████▌ | 389kB 5.9MB/s eta 0:00:01 35% [Working] 92% |█████████████████████████████▋ | 706kB 5.2MB/s eta 0:00:01 2% [Waiting for headers] Unpacking poppler-data (0.4.8-2) ... Receiving objects: 11% (613/5570) 99% [Working] 4944 kB/s 0s Unpacking libxcb-dri2-0:amd64 (1.13-1) ... 87% [18 Packages store 0 B] [19 Translation-en 3930 kB/4941 kB 80%] Unpacking libcilkrts5:amd64 (7.3.0-27ubuntu1~18.04) ... 36% [64 librsvg2-2 2614 B/98.6 kB 3%] 94% |██████████████████████████████▏ | 942kB 6.0MB/s eta 0:00:01 To activate the new configuration, you need to run: 98% [26 Packages store 0 B] 4229 kB/s 0s Unpacking libgomp1:amd64 (8.2.0-1ubuntu2~18.04) ... 40% |████████████▉ | 3.3MB 5.8MB/s eta 0:00:01 Setting up binutils-x86-64-linux-gnu (2.30-21ubuntu1~18.04) ... Preparing to unpack .../109-libnspr4_2%3a4.18-1ubuntu1_amd64.deb ... Get:2 http://security.ubuntu.com/ubuntu bionic-security InRelease [83.2 kB] Unpacking python3-keyrings.alt (3.0-1) ... Get:134 http://archive.ubuntu.com/ubuntu bionic/universe amd64 python3-pyocr all 0.3.0-1 [11.8 kB] librsvg2-bin lm-sensors speex libwmf0.2-7-gtk poppler-utils fonts-japanese-mincho | fonts-ipafont-mincho fonts-japanese-gothic 100% |████████████████████████████████| 358kB 2.0MB/s 77% |████████████████████████▉ | 5.5MB 6.0MB/s eta 0:00:01 Preparing to unpack .../040-libvdpau1_1.1.1-3ubuntu1_amd64.deb ... Selecting previously unselected package python3-dev. Setting up libdrm-amdgpu1:amd64 (2.4.91-2) ... Selecting previously unselected package dh-python. 41% |█████████████▎ | 3.0MB 5.8MB/s eta 0:00:01 100% |████████████████████████████████| 184kB 3.1MB/s Unpacking libpoppler73:amd64 (0.62.0-2ubuntu2.5) ... Get:2 http://archive.ubuntu.com/ubuntu bionic/main amd64 fontconfig-config all 2.12.6-0ubuntu2 [55.8 kB] Get:2 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libbinutils amd64 2.30-21ubuntu1~18.04 [502 kB] Preparing to unpack .../104-libllvm6.0_1%3a6.0-1ubuntu2_amd64.deb ... 1% |▎ | 81kB 2.2MB/s eta 0:00:04 Selecting previously unselected package libfontconfig1:amd64. Get:64 http://archive.ubuntu.com/ubuntu bionic/main amd64 librsvg2-2 amd64 2.40.20-2 [98.6 kB] 69% |██████████████████████▎ | 4.9MB 5.9MB/s eta 0:00:01 Enabling module alias. Selecting previously unselected package libssh-gcrypt-4:amd64. 1% [2 libbinutils 2613 B/502 kB 1%] Email address: Unpacking libpangocairo-1.0-0:amd64 (1.40.14-1ubuntu0.1) ... Running setup.py bdist_wheel for python-levenshtein ... - 4% |█▍ | 112kB 6.5MB/s eta 0:00:01 bms@pingu : ~ $ exit 41% [Waiting for headers] 4081 kB/s 12s Selecting previously unselected package python3-setuptools. Preparing to unpack .../42-libpython3.6-dev_3.6.7-1~18.04_amd64.deb ... 31% [Waiting for headers] 36% [27 g++-7 2816 kB/7570 kB 37%] 4081 kB/s 13s Selecting previously unselected package python3-keyrings.alt. Setting up libquadmath0:amd64 (8.2.0-1ubuntu2~18.04) ... Get:32 http://archive.ubuntu.com/ubuntu bionic-backports/universe Sources [2068 B] Unpacking libmemcached11:amd64 (1.0.18-4.2) ... Get:26 http://archive.ubuntu.com/ubuntu bionic/main amd64 libijs-0.35 amd64 0.35-13 [15.5 kB] (Reading database ... 15% Get:13 http://security.ubuntu.com/ubuntu bionic-security/universe amd64 Packages [113 kB] 8% |██▉ | 716kB 2.8MB/s eta 0:00:03 Selecting previously unselected package libxcb-dri3-0:amd64. Unpacking libgdk-pixbuf2.0-0:amd64 (2.36.11-2) ... 0% [5 Sources store 0 B] [3 InRelease gpgv 88.7 kB] [Waiting for headers] Get:3 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 binutils-x86-64-linux-gnu amd64 2.30-21ubuntu1~18.04 [1855 kB] 42% [Waiting for headers] 4081 kB/s 12s Applying documents.0015_add_insensitive_to_match... OK Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/docs/js/jquery.json-view.min.js' Applying documents.0019_add_consumer_user... OK Preparing to unpack .../27-g++_4%3a7.3.0-3ubuntu2.1_amd64.deb ... Set ftpupload password: * Management: https://landscape.canonical.com Unpacking mesa-va-drivers:amd64 (18.0.5-0ubuntu0~18.04.1) ... 97% [136 tesseract-ocr-osd 2747 kB/2989 kB 92%] 4630 kB/s 0s Setting up python3-pip (9.0.1-2.3~ubuntu1) ... 14% |████▋ | 92kB 5.9MB/s eta 0:00:01 Get:59 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libpango-1.0-0 amd64 1.40.14-1ubuntu0.1 [153 kB] Running setup.py bdist_wheel for pyocr ... - libpoppler-cpp0v5 libpoppler-dev libpoppler73 librsvg2-2 librsvg2-common libsensors4 libshine3 libsnappy1v5 libsoxr0 libspeex1 43% |█████████████▊ | 3.1MB 6.0MB/s eta 0:00:01 Setting up libbinutils:amd64 (2.30-21ubuntu1~18.04) ... 34% |███████████ | 2.8MB 5.9MB/s eta 0:00:01 26% [24 gcc-7 2540 kB/7455 kB 34%] 32% [Working] 93% [46 python-pip-whl 2611 B/1652 kB 0%] 4611 kB/s 0s 29% |█████████▌ | 798kB 5.9MB/s eta 0:00:01 Setting up libisl19:amd64 (0.19-1) ... Collecting termcolor==1.1.0 (from -r requirements.txt (line 72)) Selecting previously unselected package libmagickwand-6.q16-3:amd64. (Reading database ... 28583 files and directories currently installed.) Stored in directory: /root/.cache/pip/wheels/ec/0c/a9/1647275e7ef5014e7b83ff30105180e332867d65e7617ddafe Applying reminders.0001_initial... OK Not uninstalling requests at /usr/lib/python3/dist-packages, outside environment /usr Preparing to unpack .../118-libtesseract4_4.00~git2288-10f4998a-2_amd64.deb ... 5% [8 libfftw3-double3 4061 B/735 kB 1%] 40% |█████████████ | 798kB 5.8MB/s eta 0:00:01 Collecting wcwidth==0.1.7 (from -r requirements.txt (line 80)) Stored in directory: /root/.cache/pip/wheels/ff/94/8e/dccadc6bce17c41a9dbb0c7ccd44acdb9dcc0edd9efa42eaf6 Stored in directory: /root/.cache/pip/wheels/11/5d/7e/f325d21ffec71263368df56a2af6ab3c802e0e869abd4b3c03 6% |██▏ | 552kB 3.0MB/s eta 0:00:03 9% [Waiting for headers] Unpacking linux-libc-dev:amd64 (4.15.0-43.46) ... 100% |████████████████████████████████| 81kB 3.9MB/s Preparing to unpack .../44-manpages-dev_4.15-1_all.deb ... Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/docs/js/highlight.pack.js' Applying admin.0001_initial... OK Unpacking libvorbisfile3:amd64 (1.3.5-4.2) ... Selecting previously unselected package ssl-cert. Get:23 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libgcc-7-dev amd64 7.3.0-27ubuntu1~18.04 [2380 kB] 55% [Working] 3755 kB/s 8s Setting up libvorbisenc2:amd64 (1.3.5-4.2) ... System information as of Sun Jan 20 19:49:47 GMT 2019 Get:106 http://archive.ubuntu.com/ubuntu bionic/main amd64 libopenexr22 amd64 2.2.0-11.1ubuntu1 [560 kB] 16% |█████▎ | 1.2MB 5.9MB/s eta 0:00:02 Preparing to unpack .../135-tesseract-ocr-osd_4.00~git24-0e00fe6-1.2_all.deb ... 19% |██████▏ | 1.4MB 5.8MB/s eta 0:00:01 0% [1 InRelease gpgv 242 kB] [Waiting for headers] 98% [Waiting for headers] 4944 kB/s 0s Downloading https://files.pythonhosted.org/packages/71/e8/6777f6624681c8b9701a8a0a5654f3eb56919a01a78e12bf3c73f5a3c714/pyparsing-2 91% [18 Packages store 0 B] 3-py2.py3-none-any.whl (214kB) Copying '/home/paperless/paperless/src/paperless/static/paperless/img/logo-dark.png' Downloading https://files.pythonhosted.org/packages/67/08/4815a09603fc800209431bec5b8bd2acf2f95abdfb558a44a42507fb94da/apipkg-1.5- update-alternatives: using /usr/bin/composite-im6.q16 to provide /usr/bin/composite-im6 (composite-im6) in auto mode Get:24 http://archive.ubuntu.com/ubuntu bionic-updates/restricted Sources [2064 B] remote: Counting objects: 100% (21/21), done. Receiving objects: 8% (446/5570) Apply all migrations: admin, auth, contenttypes, documents, reminders, sessions Preparing to unpack .../089-libssh-gcrypt-4_0.8.0~20170825.94fa1e38-1ubuntu0.2_amd64.deb ... grads graphviz hp2xx html2ps libwmf-bin mplayer povray radiance sane-utils texlive-base-bin transfig ufraw-batch xdg-utils Preparing to unpack .../052-libcroco3_0.6.12-2_amd64.deb ... 9% |███▏ | 307kB 5.8MB/s eta 0:00:01 7% [Waiting for headers] 38% |████████████▍ | 1.2MB 5.8MB/s eta 0:00:01 28% |█████████ | 2.3MB 6.0MB/s eta 0:00:01 Setting up libfakeroot:amd64 (1.22-2ubuntu1) ... 56% [104 liblept5 4061 B/929 kB 0%] 3755 kB/s 8s 42% |█████████████▌ | 419kB 5.9MB/s eta 0:00:01 91% [18 Packages store 0 B] [22 Sources 2687 B/231 kB 1%] Processing triggers for mime-support (3.60ubuntu1) ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/km.js' Preparing to unpack .../41-libfile-fcntllock-perl_0.22-3build2_amd64.deb ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/icon-unknown-alt.svg' 62% |████████████████████▏ | 92kB 5.5MB/s eta 0:00:01 kit-2.0.7-py3-none-any.whl (338kB) Preparing to unpack .../13-libitm1_8.2.0-1ubuntu2~18.04_amd64.deb ... Note, selecting 'proftpd-basic' instead of 'proftpd' 87% [129 mesa-vdpau-drivers 4059 B/1902 kB 0%] 4630 kB/s 2s bms@pingu : ~ $ lxc launch ubuntu: paperless Get:90 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libssh-gcrypt-4 amd64 0.8.0~20170825.94fa1e38-1ubuntu0.2 [171 kB] Selecting previously unselected package libpaper1:amd64. Collecting pillow==5.3.0 (from -r requirements.txt (line 44)) Downloading https://files.pythonhosted.org/packages/24/3d/977140bd94bfb160f98a5c02fdfbb72325130f12a325cf993182956e9d0e/python_dote Selecting previously unselected package liblcms2-2:amd64. 47% |███████████████▏ | 3.8MB 5.8MB/s eta 0:00:01 Preparing to unpack .../48-python3-dev_3.6.7-1~18.04_amd64.deb ... 30% |█████████▋ | 194kB 5.9MB/s eta 0:00:01 61% |███████████████████▋ | 4.4MB 5.9MB/s eta 0:00:01 Get:127 http://archive.ubuntu.com/ubuntu bionic/main amd64 libxshmfence1 amd64 1.3-1 [5028 B] Applying auth.0006_require_contenttypes_0002... OK 4% |█▍ | 358kB 2.9MB/s eta 0:00:03 Setting up libva-drm2:amd64 (2.1.0-3) ... Building dependency tree 0 updates are security updates. 54% |█████████████████▌ | 419kB 5.8MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/pt-BR.js' Selecting previously unselected package libopus0:amd64. Preparing to unpack .../063-librsvg2-2_2.40.20-2_amd64.deb ... 49% |███████████████▉ | 1.3MB 5.8MB/s eta 0:00:01 Selecting previously unselected package libgif7:amd64. Downloading https://files.pythonhosted.org/packages/37/54/2d169a102a3727f3ebe535da9263babb88a5862516ae9a798a7e458399a6/pyocr-0.5.3 Get:33 http://archive.ubuntu.com/ubuntu bionic-backports/universe amd64 Packages [3472 B] remote: Enumerating objects: 21, done. Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/fonts/Roboto-Bold-webfont.woff' Unpacking libgif7:amd64 (5.1.4-2) ... 35% |███████████▎ | 2.9MB 5.9MB/s eta 0:00:01 99% |████████████████████████████████| 7.1MB 39.8MB/s eta 0:00:01 5% |█▉ | 112kB 6.5MB/s eta 0:00:01 Collecting python-dateutil==2.7.5 (from -r requirements.txt (line 61)) Preparing to unpack .../33-python3-distutils_3.6.7-1~18.04_all.deb ... Applying documents.0007_auto_20160126_2114... OK Unpacking libc6-dev:amd64 (2.27-3ubuntu1) ... Get:70 http://archive.ubuntu.com/ubuntu bionic/main amd64 libtheora0 amd64 1.1.1+dfsg.1-14 [170 kB] 2% |▊ | 174kB 771kB/s eta 0:00:11 Selecting previously unselected package gsfonts. 19% [Waiting for headers] Unpacking libopenjp2-7:amd64 (2.3.0-1) ... 29% [Waiting for headers] Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/prepopulate.min.js' Setting up apache2-utils (2.4.29-1ubuntu4.5) ... Selecting previously unselected package libcrystalhd3:amd64. 99% |████████████████████████████████| 8.1MB 6.0MB/s eta 0:00:01 Running setup.py bdist_wheel for filemagic ... done Get:30 http://archive.ubuntu.com/ubuntu bionic-updates/multiverse amd64 Packages [6376 B] 60% |███████████████████▍ | 1.2MB 5.1MB/s eta 0:00:01 Get:16 http://security.ubuntu.com/ubuntu bionic-security/multiverse Translation-en [996 B] 100% |████████████████████████████████| 92kB 3.8MB/s 96% |██████████████████████████████▉ | 890kB 5.2MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/admin/DateTimeShortcuts.js' Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/icon-unknown.svg' Selecting previously unselected package librsvg2-common:amd64. Collecting idna==2.7 (from -r requirements.txt (line 29)) 100% |████████████████████████████████| 71kB 4.2MB/s .3.0-py2.py3-none-any.whl Setting up libogg0:amd64 (1.3.2-1) ... 37% |████████████ | 317kB 6.7MB/s eta 0:00:01 Unpacking libwmf0.2-7:amd64 (0.2.8.4-12) ... Get:87 http://archive.ubuntu.com/ubuntu bionic/main amd64 libmpg123-0 amd64 1.25.10-1 [125 kB] Unpacking binutils-common:amd64 (2.30-21ubuntu1~18.04) ... Starting paperless Unpacking libavformat57:amd64 (7:3.4.4-0ubuntu0.18.04.1) ... Setting up libva2:amd64 (2.1.0-3) ... Selecting previously unselected package libdrm-nouveau2:amd64. Collecting django-crispy-forms==1.7.2 (from -r requirements.txt (line 15)) Selecting previously unselected package libvdpau1:amd64. 53% |█████████████████▎ | 3.8MB 6.0MB/s eta 0:00:01 .3.2-py2.py3-none-any.whl (74kB) Setting up binutils (2.30-21ubuntu1~18.04) ... 31% [43 libpixman-1-0 2613 B/229 kB 1%] Downloading https://files.pythonhosted.org/packages/35/e0/e9e83b244eaa382ba21896dda6172617e47aff0be225eb72782cca105d3c/Sphinx-1.8. Get:39 http://archive.ubuntu.com/ubuntu bionic/main amd64 libalgorithm-diff-xs-perl amd64 0.04-5 [11.1 kB] 33% [27 g++-7 2611 B/7570 kB 0%] 4081 kB/s 14s 0-py2.py3-none-any.whl Preparing to unpack .../022-libavahi-client3_0.7-3.1ubuntu1.1_amd64.deb ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/icon-changelink.svg' Selecting previously unselected package libpciaccess0:amd64. 35% [Waiting for headers] Collecting parso==0.3.1 (from -r requirements.txt (line 40)) Unpacking libaprutil1-ldap:amd64 (1.6.1-2) ... Preparing to unpack .../56-python3-wheel_0.30.0-0.2_all.deb ... Applying documents.0014_document_checksum... OK Get:69 http://archive.ubuntu.com/ubuntu bionic-updates/universe amd64 libswresample2 amd64 7:3.4.4-0ubuntu0.18.04.1 [55.2 kB] .3.0-py2.py3-none-any.whl (59kB) Enter new UNIX password: Setting up apache2-bin (2.4.29-1ubuntu4.5) ... libxcb-sync1 libxcb-xfixes0 libxfixes3 libxrender1 libxshmfence1 libxvidcore4 libzvbi-common libzvbi0 mesa-va-drivers Setting up libc6-dev:amd64 (2.27-3ubuntu1) ... Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/css/prettify.css' 79% |█████████████████████████▎ | 6.4MB 5.8MB/s eta 0:00:01 90% |█████████████████████████████ | 307kB 5.8MB/s eta 0:00:01 10% [Waiting for headers] 92% |█████████████████████████████▌ | 71kB 5.9MB/s eta 0:00:01 .1.1.tar.gz (112kB) Unpacking libswresample2:amd64 (7:3.4.4-0ubuntu0.18.04.1) ... 36% [Waiting for headers] Applying documents.0001_initial... OK 35% |███████████▌ | 194kB 5.9MB/s eta 0:00:01 Unpacking gcc-7-base:amd64 (7.3.0-27ubuntu1~18.04) ... 29% |█████████▍ | 2.4MB 5.0MB/s eta 0:00:02 Selecting previously unselected package libvpx5:amd64. Collecting langdetect==1.0.7 (from -r requirements.txt (line 36)) Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/pt.js' Get:45 http://archive.ubuntu.com/ubuntu bionic/main amd64 manpages-dev all 4.15-1 [2217 kB] Unpacking libavutil55:amd64 (7:3.4.4-0ubuntu0.18.04.1) ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/is.js' Get:22 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libquadmath0 amd64 8.2.0-1ubuntu2~18.04 [133 kB] Setting up libvpx5:amd64 (1.7.0-3) ... Receiving objects: 22% (1226/5570), 676.01 KiB | 1.30 MiB/s Setting up libitm1:amd64 (8.2.0-1ubuntu2~18.04) ... 84% [43 libpython3.6-dev 38.4 MB/44.8 MB 86%] 4611 kB/s 2s 18% [16 libasan4 2613 B/358 kB 1%] Setting up libstdc++-7-dev:amd64 (7.3.0-27ubuntu1~18.04) ... Get:108 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libmagickcore-6.q16-3-extra amd64 8:6.9.7.4+dfsg-16ubuntu6.4 [62. Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/tooltag-arrowright.svg' 95% [Waiting for headers] 4611 kB/s 0s Get:57 http://archive.ubuntu.com/ubuntu bionic/main amd64 libdatrie1 amd64 0.2.10-7 [17.8 kB] Get:115 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libpoppler-dev amd64 0.62.0-2ubuntu2.5 [4616 B] Unpacking libfftw3-double3:amd64 (3.3.7-1) ... Adding user `ftpupload' ... Setting up libnetpbm10 (2:10.0-15.3build1) ... ode-1.2-py2.py3-none-any.whl (77kB) Preparing to unpack .../34-dh-python_3.20180325ubuntu2_all.deb ... Setting up python3-setuptools (39.0.1-2) ... Setting up libcilkrts5:amd64 (7.3.0-27ubuntu1~18.04) ... psycopg/docs/install.html#binary-install-from-pypi>. Selecting previously unselected package libwebpdemux2:amd64. (Reading database ... 35% 17% |█████▊ | 1.3MB 6.0MB/s eta 0:00:01 Setting up libgsm1:amd64 (1.0.13-4build1) ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/calendar.js' Get:33 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 python3-lib2to3 all 3.6.7-1~18.04 [76.5 kB] i965-va-driver imagemagick imagemagick-6-common imagemagick-6.q16 libaacs0 libavahi-client3 libavahi-common-data 58% |██████████████████▊ | 4.2MB 5.8MB/s eta 0:00:01 1% |▍ | 81kB 5.1MB/s eta 0:00:02 t-sugar, regex, termcolor, tzlocal, python-levenshtein Get:28 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 g++ amd64 4:7.3.0-3ubuntu2.1 [1572 B] 6% [Waiting for headers] 12% |████ | 1.0MB 3.5MB/s eta 0:00:03 Downloading https://files.pythonhosted.org/packages/74/68/d87d9b36af36f44254a8d512cbfc48369103a3b9e474be9bdfe536abfc45/python_date 45% |██████████████▋ | 3.3MB 5.9MB/s eta 0:00:01 81% [43 libpython3.6-dev 35.6 MB/44.8 MB 79%] 4611 kB/s 3s 0.7.0-py2.py3-none-any.whl (357kB) Selecting previously unselected package libwmf0.2-7:amd64. Get:4 http://archive.ubuntu.com/ubuntu bionic/main amd64 libaprutil1-ldap amd64 1.6.1-2 [8764 B] 97% |███████████████████████████████ | 2.6MB 5.9MB/s eta 0:00:01 Setting up cpp-7 (7.3.0-27ubuntu1~18.04) ... 100% |████████████████████████████████| 3.1MB 338kB/s 12% [Working] Downloading https://files.pythonhosted.org/packages/fa/bc/9bd3b5c2b4774d5f33b2d544f1460be9df7df2fe42f352135381c347c69a/ipython_gen Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/autocomplete.js' Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/js/csrf.js' Unpacking libpython3-dev:amd64 (3.6.7-1~18.04) ... 30% [Waiting for headers] Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/SelectBox.js' Setting up libxcb-dri3-0:amd64 (1.13-1) ... Unpacking cpp (4:7.3.0-3ubuntu2.1) ... 59% |███████████████████ | 4.2MB 6.0MB/s eta 0:00:01 Get:6 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 apache2-bin amd64 2.4.29-1ubuntu4.5 [1071 kB] 0% [1 libmemcached11 2614 B/83.3 kB 3%] 40% [Waiting for headers] 3755 kB/s 10s Get:16 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libasan4 amd64 7.3.0-27ubuntu1~18.04 [358 kB] Collecting attrs==18.2.0 (from -r requirements.txt (line 5)) Preparing to unpack .../068-libswresample2_7%3a3.4.4-0ubuntu0.18.04.1_amd64.deb ... Running setup.py bdist_wheel for pdftotext ... - 77% [110 libnspr4 1165 B/112 kB 1%] 3755 kB/s 3s 61% [43 libpython3.6-dev 14.5 MB/44.8 MB 32%] 4081 kB/s 8s 21% |██████▊ | 194kB 6.0MB/s eta 0:00:01 Running setup.py bdist_wheel for python-levenshtein ... / Unpacking hicolor-icon-theme (0.17-2) ... 95% |██████████████████████████████▊ | 194kB 5.9MB/s eta 0:00:01 37% |████████████ | 3.1MB 5.9MB/s eta 0:00:01 Preparing to unpack .../139-va-driver-all_2.1.0-3_amd64.deb ... Downloading https://files.pythonhosted.org/packages/55/80/4bbd33f6d6b305c509a90b37c1cf7255000344f513b36827ec2c17af5ad5/inotify_sim update-alternatives: using /usr/bin/montage-im6.q16 to provide /usr/bin/montage-im6 (montage-im6) in auto mode Setting up dpkg-dev (1.19.0.5ubuntu2.1) ... 3% [Waiting for headers] Found existing installation: Pillow 5.1.0 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/tooltag-add.svg' Selecting previously unselected package fontconfig-config. Setting up libmpg123-0:amd64 (1.25.10-1) ... Preparing to unpack .../134-tesseract-ocr-eng_4.00~git24-0e00fe6-1.2_all.deb ... 0% [1 InRelease gpgv 242 kB] Unpacking imagemagick-6.q16 (8:6.9.7.4+dfsg-16ubuntu6.4) ... psycopg2, py, pycodestyle, pyocr, pytest, pytest-cov, pytest-django, pytest-env, pytest-forked, termcolor, pytest-sugar, pytest-xdis Selecting previously unselected package libfftw3-double3:amd64. Collecting python-gnupg==0.4.3 (from -r requirements.txt (line 63)) Setting up libvdpau1:amd64 (1.1.1-3ubuntu1) ... Setting up python3-keyring (10.6.0-1) ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/sr.js' Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/css/forms.css' Preparing to unpack .../028-libgs9-common_9.26~dfsg+0-0ubuntu0.18.04.3_all.deb ... Setting up libvorbis0a:amd64 (1.3.5-4.2) ... Preparing to unpack .../026-libjbig2dec0_0.13-6_amd64.deb ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/ru.js' 17% [Waiting for headers] Selecting previously unselected package libgdk-pixbuf2.0-0:amd64. Selecting previously unselected package libgdk-pixbuf2.0-bin. Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/inline-delete.svg' Setting up libbluray2:amd64 (1:1.0.2-3) ... 8% [7 libc6-dev 2563 kB/2587 kB 99%] 37% |████████████ | 1.0MB 6.0MB/s eta 0:00:01 74% |███████████████████████▉ | 2.3MB 5.8MB/s eta 0:00:01 17% |█████▌ | 1.4MB 5.1MB/s eta 0:00:02 51% |████████████████▋ | 1.6MB 5.8MB/s eta 0:00:01 Selecting previously unselected package libvorbisenc2:amd64. Unpacking python3-secretstorage (2.3.1-2) ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/az.js' bms@FortBoyard:~$ ssh pingu.lan Setting up libcairo2:amd64 (1.15.10-2) ... 92% |█████████████████████████████▋ | 501kB 5.8MB/s eta 0:00:01 Preparing to unpack .../007-libfftw3-double3_3.3.7-1_amd64.deb ... )) Unpacking python3-pip (9.0.1-2.3~ubuntu1) ... Reading package lists... 83% 35% [Waiting for headers] Unpacking libtiff5:amd64 (4.0.9-5) ... 77% [Waiting for headers] 3755 kB/s 3s Preparing to unpack .../077-libx264-152_2%3a0.152.2854+gite9a5903-2_amd64.deb ... 31% |██████████ | 112kB 6.4MB/s eta 0:00:01 Setting up libxcb-shm0:amd64 (1.13-1) ... Get:62 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libpangoft2-1.0-0 amd64 1.40.14-1ubuntu0.1 [33.2 kB] Unpacking libopenexr22:amd64 (2.2.0-11.1ubuntu1) ... update-alternatives: using /usr/bin/animate-im6.q16 to provide /usr/bin/animate (animate) in auto mode Selecting previously unselected package libmagickcore-6.q16-3-extra:amd64. Setting up libopenmpt0:amd64 (0.3.6-1) ... Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/js/coreapi-0.1.1.js' Selecting previously unselected package libswresample2:amd64. Get:121 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libx11-xcb1 amd64 2:1.6.4-3ubuntu0.1 [9560 B] Unpacking libtsan0:amd64 (8.2.0-1ubuntu2~18.04) ... Preparing to unpack .../086-libmpg123-0_1.25.10-1_amd64.deb ... Get:57 http://archive.ubuntu.com/ubuntu bionic/universe amd64 python3-wheel all 0.30.0-0.2 [36.5 kB] 97% [18 Packages store 0 B] Running setup.py bdist_wheel for docopt ... done Collecting gunicorn==19.9.0 (from -r requirements.txt (line 28)) Running setup.py bdist_wheel for regex ... - 22% |███████▎ | 1.8MB 5.6MB/s eta 0:00:02 41% [Working] 4081 kB/s 12s bms@FortBoyard:~$ Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/docs/css/base.css' bms@FortBoyard:~$ e bms@pingu : ~ $ lxc exec paperless -- sh -c "wget https://raw.githubusercontent.com/bmsleight/paperless/master/scripts/lxc/lxc-install. 21% [8 Sources store 0 B] [10 Sources 686 kB/9051 kB 8%] [11 Packages 134 kB/242 kB 56%] 50% [Waiting for headers] 3755 kB/s 9s 75% |████████████████████████▏ | 6.1MB 5.8MB/s eta 0:00:01 Get:140 http://archive.ubuntu.com/ubuntu bionic/universe amd64 va-driver-all amd64 2.1.0-3 [4376 B] 50% [86 libgme0 1165 B/121 kB 1%] 3755 kB/s 8s 37% [Waiting for headers] 56% |██████████████████ | 1.5MB 6.0MB/s eta 0:00:01 Requirement already satisfied: setuptools>=18.5 in /usr/lib/python3/dist-packages (from ipython==7.1.1->-r requirements.txt (line 33 ed-0.2-py2.py3-none-any.whl 18% [Waiting for headers] Enabling conf charset. Get:31 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 dpkg-dev all 1.19.0.5ubuntu2.1 [608 kB] 36% [Waiting for headers] Retype new UNIX password: 9% [9 libisl19 2613 B/551 kB 0%] openbsd-inetd | inet-superserver proftpd-mod-ldap proftpd-mod-mysql proftpd-mod-odbc proftpd-mod-pgsql proftpd-mod-sqlite Preparing to unpack .../52-python3-pip_9.0.1-2.3~ubuntu1_all.deb ... Password (again): 0% [5 Sources store 0 B] [3 InRelease gpgv 88.7 kB] [8 Sources 2687 B/181 kB 1%] gnupg is already the newest version (2.2.4-1ubuntu1.2). Get:21 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libmpx2 amd64 8.2.0-1ubuntu2~18.04 [11.7 kB] Unpacking libgdk-pixbuf2.0-bin (2.36.11-2) ... libmemcached11 libmemcachedutil2 proftpd-basic proftpd-doc Selecting previously unselected package libgme0:amd64. Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/change_form.js' .0-py2.py3-none-any.whl Setting up libthai-data (0.1.27-2) ... Setting up libdrm-nouveau2:amd64 (2.4.91-2) ... Setting up imagemagick-6.q16 (8:6.9.7.4+dfsg-16ubuntu6.4) ... 55% |█████████████████▊ | 1.7MB 6.0MB/s eta 0:00:01 Get:138 http://archive.ubuntu.com/ubuntu bionic/universe amd64 unpaper amd64 6.1-2 [242 kB] Cloning into 'paperless'... Selecting previously unselected package libavformat57:amd64. Setting up libwavpack1:amd64 (5.1.0-2ubuntu1.2) ... Unpacking libnspr4:amd64 (2:4.18-1ubuntu1) ... bms@pingu : ~ $ # Run the install script 48% [43 libpython3.6-dev 4057 B/44.8 MB 0%] 4081 kB/s 12s g-0.4.3-py2.py3-none-any.whl Receiving objects: 4% (223/5570) 20% |██████▋ | 399kB 5.9MB/s eta 0:00:01 Usage of /: 2.9% of 2.68TB IP address for br0: 192.168.1.227 44% [Waiting for headers] 3755 kB/s 10s Selecting previously unselected package libc-dev-bin. 63% |████████████████████▍ | 4.5MB 6.0MB/s eta 0:00:01 Processing triggers for libgdk-pixbuf2.0-0:amd64 (2.36.11-2) ... 27% |████████▉ | 2.0MB 6.0MB/s eta 0:00:01 Preparing to unpack .../11-cpp_4%3a7.3.0-3ubuntu2.1_amd64.deb ... Preparing to unpack .../074-libwavpack1_5.1.0-2ubuntu1.2_amd64.deb ... 33% [Waiting for headers] Get:5 http://archive.ubuntu.com/ubuntu bionic/main amd64 fonts-droid-fallback all 1:6.0.1r16-1.1 [1805 kB] 0 upgraded, 58 newly installed, 0 to remove and 19 not upgraded. Unpacking libpixman-1-0:amd64 (0.34.0-2) ... Get:24 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libcups2 amd64 2.2.7-1ubuntu2.3 [211 kB] Applying documents.0013_auto_20160325_2111... OK Downloading https://files.pythonhosted.org/packages/ef/7a/212184e2e1e7f2e003b0b109c737f69518830d9c4e8d6cd9a33d17ec48f6/django_exte 65% [105 libllvm6.0 5753 kB/14.5 MB 40%] 3755 kB/s 6s Selecting previously unselected package libshine3:amd64. Applying auth.0004_alter_user_username_opts... OK -2.11.1-py2.py3-none-any.whl 51% [89 libopenmpt0 2613 B/561 kB 0%] 3755 kB/s 8s Unpacking netpbm (2:10.0-15.3build1) ... Collecting pytest-xdist==1.24.0 (from -r requirements.txt (line 59)) Get:4 http://archive.ubuntu.com/ubuntu bionic/main amd64 fontconfig amd64 2.12.6-0ubuntu2 [169 kB] Unpacking libtwolame0:amd64 (0.3.13-3) ... 86% |███████████████████████████▊ | 51kB 5.5MB/s eta 0:00:01 Selecting previously unselected package gcc-7-base:amd64. Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/css/widgets.css' Setting up g++-7 (7.3.0-27ubuntu1~18.04) ... 100% |████████████████████████████████| 8.1MB 130kB/s 49% [10 Sources store 0 B] [Waiting for headers] 55% |█████████████████▊ | 1.1MB 6.0MB/s eta 0:00:01 13% [Waiting for headers] Unpacking libavahi-common-data:amd64 (0.7-3.1ubuntu1.1) ... Running setup.py bdist_wheel for inotify-simple ... done 9% [Waiting for headers] 33% |██████████▋ | 112kB 6.4MB/s eta 0:00:01 Unpacking libcairo2:amd64 (1.15.10-2) ... 62% |████████████████████ | 4.5MB 6.0MB/s eta 0:00:01 100% |████████████████████████████████| 931kB 1.0MB/s 71% |███████████████████████ | 5.8MB 5.9MB/s eta 0:00:01 Get:56 http://archive.ubuntu.com/ubuntu bionic-updates/universe amd64 python3-venv amd64 3.6.7-1~18.04 [1208 B] 53% |█████████████████▏ | 409kB 5.2MB/s eta 0:00:01 Setting up dh-python (3.20180325ubuntu2) ... 15% |█████▏ | 307kB 5.9MB/s eta 0:00:01 Unpacking libgs9-common (9.26~dfsg+0-0ubuntu0.18.04.3) ... 44% |██████████████▍ | 1.4MB 5.9MB/s eta 0:00:01 Running setup.py bdist_wheel for regex ... \ Applying contenttypes.0002_remove_content_type_name... OK Unpacking python3-olefile (0.45.1-1) ... Copying '/usr/local/lib/python3.6/dist-packages/django_extensions/static/django_extensions/js/jquery.autocomplete.js' 56% [103 libilmbase12 2614 B/71.4 kB 4%] 3755 kB/s 8s Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/lv.js' Unpacking libapr1:amd64 (1.6.3-2) ... 21% |██████▉ | 1.7MB 4.1MB/s eta 0:00:02 90% |████████████████████████████▉ | 7.3MB 5.9MB/s eta 0:00:01 Get:61 http://archive.ubuntu.com/ubuntu bionic/main amd64 libharfbuzz0b amd64 1.7.2-1ubuntu1 [232 kB] 100% |████████████████████████████████| 51kB 3.4MB/s Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/mk.js' Superuser created successfully. 28% [Working] apache2 apache2-bin apache2-data apache2-utils libapache2-mod-wsgi-py3 libapr1 libaprutil1 libaprutil1-dbd-sqlite3 Fetched 26.6 MB in 8s (3350 kB/s) 13% [Waiting for headers] tensions, django, django-filter, djangorestframework, docutils, execnet, text-unidecode, faker, factory-boy, filelock, filemagic, py Get:41 http://archive.ubuntu.com/ubuntu bionic/main amd64 libvdpau1 amd64 1.1.1-3ubuntu1 [25.5 kB] 45% |██████████████▋ | 1.2MB 6.0MB/s eta 0:00:01 Setting up va-driver-all:amd64 (2.1.0-3) ... 94% [136 tesseract-ocr-osd 2611 B/2989 kB 0%] 4630 kB/s 0s Unpacking libpaper-utils (1.1.24+nmu5ubuntu1) ... Preparing to unpack .../04-libc-dev-bin_2.27-3ubuntu1_amd64.deb ... Get:58 http://archive.ubuntu.com/ubuntu bionic/main amd64 libthai0 amd64 0.1.27-2 [18.0 kB] Get:136 http://archive.ubuntu.com/ubuntu bionic/universe amd64 tesseract-ocr-osd all 4.00~git24-0e00fe6-1.2 [2989 kB] Preparing to unpack .../060-libharfbuzz0b_1.7.2-1ubuntu1_amd64.deb ... Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/js/bootstrap.min.js' Preconfiguring packages ... 14-py3-none-any.whl (543kB) Enabling module authz_user. 41% [79 libx265-146 2612 B/1026 kB 0%] 3755 kB/s 10s update-alternatives: using /usr/bin/compare-im6.q16 to provide /usr/bin/compare (compare) in auto mode 86% [43 libpython3.6-dev 41.1 MB/44.8 MB 92%] 4611 kB/s 1s Get:48 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 python3.6-dev amd64 3.6.7-1~18.04 [508 kB] 79% [113 libpoppler73 2613 B/800 kB 0%] 4630 kB/s 2s Unpacking libxshmfence1:amd64 (1.3-1) ... 61% [105 libllvm6.0 2877 kB/14.5 MB 20%] 3755 kB/s 7s 29% |█████████▍ | 911kB 5.2MB/s eta 0:00:01 35% |███████████▎ | 1.1MB 6.0MB/s eta 0:00:01 Collecting dateparser==0.7.0 (from -r requirements.txt (line 12)) Requirement already satisfied: chardet==3.0.4 in /usr/lib/python3/dist-packages (from -r requirements.txt (line 9)) Enabling module wsgi. sh && /bin/bash lxc-install.sh" Preparing to unpack .../025-libijs-0.35_0.35-13_amd64.deb ... Get:2 http://archive.ubuntu.com/ubuntu bionic/main amd64 libmemcachedutil2 amd64 1.0.18-4.2 [9528 B] Unpacking libmagickcore-6.q16-3:amd64 (8:6.9.7.4+dfsg-16ubuntu6.4) ... Reading package lists... 0% ols-4.3.0-py3-none-any.whl (48kB) 90% |████████████████████████████▉ | 6.4MB 5.9MB/s eta 0:00:01 bms@pingu : ~ $ exit Get:1 http://archive.ubuntu.com/ubuntu bionic/main amd64 libapr1 amd64 1.6.3-2 [90.9 kB] Setting up libfontconfig1:amd64 (2.12.6-0ubuntu2) ... Selecting previously unselected package proftpd-doc. 94% [46 python-pip-whl 1587 kB/1652 kB 96%] 4611 kB/s 0s Collecting imagesize==1.1.0 (from -r requirements.txt (line 30)) Enabling conf localized-error-pages. Selecting previously unselected package libgdk-pixbuf2.0-common. Selecting previously unselected package tesseract-ocr-osd. Setting up libilmbase12:amd64 (2.2.0-11ubuntu2) ... Receiving objects: 23% (1288/5570), 2.15 MiB | 2.14 MiB/s Preparing to unpack .../034-imagemagick_8%3a6.9.7.4+dfsg-16ubuntu6.4_amd64.deb ... Preparing to unpack .../114-libpoppler-dev_0.62.0-2ubuntu2.5_amd64.deb ... Selecting previously unselected package libx11-xcb1:amd64. Resolving deltas: 100% (3560/3560), done. Preparing to unpack .../029-libgs9_9.26~dfsg+0-0ubuntu0.18.04.3_amd64.deb ... Selecting previously unselected package tesseract-ocr-eng. 0% [5 Sources store 0 B] [4 InRelease gpgv 74.6 kB] [10 Sources 126 kB/9051 kB 1%] [11 Packages 1239 B/242 kB 1%] Enter new UNIX password: Receiving objects: 64% (3565/5570), 4.67 MiB | 3.10 MiB/s 38% [Working] 3755 kB/s 11s Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/css/vendor/select2/select2.min.css' Setting up libva-x11-2:amd64 (2.1.0-3) ... Preparing to unpack .../101-libgif7_5.1.4-2_amd64.deb ... Selecting previously unselected package libfakeroot:amd64. 84% [128 mesa-va-drivers 2611 B/1778 kB 0%] 4630 kB/s 2s Downloading https://files.pythonhosted.org/packages/fc/b6/aef66b4c52a6ad6ac18cf6ebc5731ed06d8c9ae4d3b2d9951f261150be67/imagesize-1 Preparing to unpack .../55-python3-venv_3.6.7-1~18.04_amd64.deb ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/urlify.js' Preparing to unpack .../12-libcc1-0_8.2.0-1ubuntu2~18.04_amd64.deb ... Setting up g++ (4:7.3.0-3ubuntu2.1) ... Copying '/usr/local/lib/python3.6/dist-packages/django_extensions/static/django_extensions/img/indicator.gif' Length: 3054 (3.0K) [text/plain] 21% [Waiting for headers] Setting up libpango-1.0-0:amd64 (1.40.14-1ubuntu0.1) ... Unpacking libmpg123-0:amd64 (1.25.10-1) ... - Reduce system reboots and improve kernel security. Activate at: Downloading https://files.pythonhosted.org/packages/f9/76/3343e69a2a1602052f587898934e5fea395d22310d39c07955596597227c/execnet-1.5 Collecting django==2.0.9 (from -r requirements.txt (line 18)) Enabling module negotiation. 1% [Working] Preparing to unpack .../20-libmpx2_8.2.0-1ubuntu2~18.04_amd64.deb ... Setting up libalgorithm-diff-xs-perl (0.04-5) ... 1% [Waiting for headers] Unpacking libvdpau1:amd64 (1.1.1-3ubuntu1) ... Selecting previously unselected package libc6-dev:amd64. Requirement already satisfied: six==1.11.0 in /usr/lib/python3/dist-packages (from -r requirements.txt (line 68)) Setting up python3.6-venv (3.6.7-1~18.04) ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/nl.js' Preparing to unpack .../041-libavutil55_7%3a3.4.4-0ubuntu0.18.04.1_amd64.deb ... ramework-3.9.0-py2.py3-none-any.whl (924kB) Adding system user `proftpd' (UID 111) ... Get:2 http://archive.ubuntu.com/ubuntu bionic/main amd64 libaprutil1 amd64 1.6.1-2 [84.4 kB] 41% [78 libx264-152 2613 B/609 kB 0%] 3755 kB/s 10s Get:98 http://archive.ubuntu.com/ubuntu bionic/main amd64 libdrm-intel1 amd64 2.4.91-2 [59.8 kB] Unpacking libnetpbm10 (2:10.0-15.3build1) ... Selecting previously unselected package libharfbuzz0b:amd64. Collecting toml==0.10.0 (from -r requirements.txt (line 74)) Preparing to unpack .../23-gcc-7_7.3.0-27ubuntu1~18.04_amd64.deb ... 100% |████████████████████████████████| 51kB 3.2MB/s 0% [2 InRelease gpgv 83.2 kB] [5 Sources 2687 B/829 kB 0%] Unpacking libexpat1-dev:amd64 (2.2.5-3) ... Enabling site 000-default. Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/css/bootstrap.min.css' Unpacking libxcb-sync1:amd64 (1.13-1) ... Setting up fonts-dejavu-core (2.37-1) ... Unpacking proftpd-doc (1.3.5e-1build1) ... Downloading https://files.pythonhosted.org/packages/10/ad/00b090d23a222943eb0eda509720a404f531a439e803f6538f35136cae9e/alabaster-0 44% |██████████████▎ | 286kB 5.9MB/s eta 0:00:01 74% |███████████████████████▊ | 686kB 6.0MB/s eta 0:00:01 53% |█████████████████▏ | 4.3MB 5.9MB/s eta 0:00:01 Unpacking dpkg-dev (1.19.0.5ubuntu2.1) ... Downloading https://files.pythonhosted.org/packages/3d/a0/b12090c40e0b8196b973962ec71c1c541a6c04af58ba5ad85683b3de251a/coverage-4. Preparing to unpack .../098-libdrm-nouveau2_2.4.91-2_amd64.deb ... Preparing to unpack .../103-liblept5_1.75.3-3_amd64.deb ... 63% |████████████████████▎ | 112kB 6.4MB/s eta 0:00:01 Setting up libpython3.6-dev:amd64 (3.6.7-1~18.04) ... 74% |████████████████████████ | 5.3MB 5.8MB/s eta 0:00:01 Preparing to unpack .../100-libgdk-pixbuf2.0-bin_2.36.11-2_amd64.deb ... 37% |████████████ | 2.7MB 6.0MB/s eta 0:00:01 Selecting previously unselected package libasan4:amd64. Setting up libmemcached11:amd64 (1.0.18-4.2) ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/css/responsive.css' 30% [10 Sources 2699 kB/9051 kB 30%] [Waiting for headers] Adding new user `proftpd' (UID 111) with group `nogroup' ... Get:124 http://archive.ubuntu.com/ubuntu bionic/main amd64 libxcb-present0 amd64 1.13-1 [5540 B] 40% [76 libwebp6 2613 B/185 kB 1%] 3755 kB/s 10s Enabling module dir. Get:3 http://archive.ubuntu.com/ubuntu bionic-updates InRelease [88.7 kB] Selecting previously unselected package mesa-va-drivers:amd64. Need to get 85.2 MB of archives. Get:63 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libpangocairo-1.0-0 amd64 1.40.14-1ubuntu0.1 [20.8 kB] Unpacking libalgorithm-diff-perl (1.19.03-1) ... 0% [1 InRelease gpgv 242 kB] [3 InRelease 2602 B/88.7 kB 3%] [2 InRelease 17.1 kB/83.2 kB 21%] 30% [Working] apache2-bin apache2-data apache2-utils libapr1 libaprutil1 libaprutil1-dbd-sqlite3 libaprutil1-ldap liblua5.2-0 ssl-cert Preparing to unpack .../062-libpangocairo-1.0-0_1.40.14-1ubuntu0.1_amd64.deb ... Setting up optipng (0.7.6-1.1) ... 4.1-py2.py3-none-any.whl (118kB) Unpacking libalgorithm-diff-xs-perl (0.04-5) ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/sr-Cyrl.js' update-alternatives: using /usr/bin/convert-im6.q16 to provide /usr/bin/convert (convert) in auto mode 49% [10 Sources store 0 B] 96% [Working] 4944 kB/s 0s Setting up libcc1-0:amd64 (8.2.0-1ubuntu2~18.04) ... Running setup.py bdist_wheel for langdetect ... \ 77% |████████████████████████▉ | 2.4MB 6.0MB/s eta 0:00:01 Creating paperless Preparing to unpack .../011-liblqr-1-0_0.4.2-2.1_amd64.deb ... Get:89 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libopenmpt0 amd64 0.3.6-1 [561 kB] Unpacking libcroco3:amd64 (0.6.12-2) ... 75% [10 Sources store 0 B] [19 Translation-en 2685 B/4941 kB 0%] Preparing to unpack .../049-libmp3lame0_3.100-2_amd64.deb ... Setting up ghostscript (9.26~dfsg+0-0ubuntu0.18.04.3) ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/da.js' Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/fr.js' Setting up proftpd-doc (1.3.5e-1build1) ... 68% |█████████████████████▊ | 1.8MB 6.0MB/s eta 0:00:01 Get:91 http://archive.ubuntu.com/ubuntu bionic-updates/universe amd64 libavformat57 amd64 7:3.4.4-0ubuntu0.18.04.1 [949 kB] Unpacking libopenmpt0:amd64 (0.3.6-1) ... Preparing to unpack .../001-fontconfig-config_2.12.6-0ubuntu2_all.deb ... Selecting previously unselected package libchromaprint1:amd64. Get:11 http://security.ubuntu.com/ubuntu bionic-security/main amd64 Packages [242 kB] Unpacking libdpkg-perl (1.19.0.5ubuntu2.1) ... Need to get 3273 kB of archives. Preparing to unpack .../039-libva-x11-2_2.1.0-3_amd64.deb ... Selecting previously unselected package libavcodec57:amd64. Preparing to unpack .../51-python3-keyrings.alt_3.0-1_all.deb ... Selecting previously unselected package libopenmpt0:amd64. ice. 28% [34 imagemagick-6.q16 2613 B/423 kB 1%] Preparing to unpack .../04-liblua5.2-0_5.2.4-1.1build1_amd64.deb ... Get:46 http://archive.ubuntu.com/ubuntu bionic/main amd64 libxrender1 amd64 1:0.9.10-1 [18.7 kB] 74% |███████████████████████▊ | 6.0MB 5.8MB/s eta 0:00:01 Setting up libssh-gcrypt-4:amd64 (0.8.0~20170825.94fa1e38-1ubuntu0.2) ... Get:7 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libgomp1 amd64 8.2.0-1ubuntu2~18.04 [76.4 kB] After this operation, 7262 kB of additional disk space will be used. Unpacking unpaper (6.1-2) ... 13% [24 libcups2 2613 B/211 kB 1%] Collecting coverage==4.5.1 (from -r requirements.txt (line 10)) Applying auth.0008_alter_user_username_max_length... OK Get:55 http://archive.ubuntu.com/ubuntu bionic-updates/universe amd64 python3.6-venv amd64 3.6.7-1~18.04 [6184 B] Get:34 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 imagemagick-6.q16 amd64 8:6.9.7.4+dfsg-16ubuntu6.4 [423 kB] (Reading database ... 0% [3 InRelease gpgv 88.7 kB] [5 Sources 598 kB/829 kB 72%] Not uninstalling attrs at /usr/lib/python3/dist-packages, outside environment /usr 78% |█████████████████████████▏ | 2.1MB 5.8MB/s eta 0:00:01 21% [6 Sources store 0 B] [10 Sources 625 kB/9051 kB 7%] [11 Packages 119 kB/242 kB 49%] Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/icon-deletelink.svg' Setting up libaprutil1-ldap:amd64 (1.6.1-2) ... 71% |██████████████████████▉ | 2.2MB 6.0MB/s eta 0:00:01 Get:12 http://security.ubuntu.com/ubuntu bionic-security/main Translation-en [91.6 kB] 8% |██▋ | 583kB 5.9MB/s eta 0:00:02 49% |████████████████ | 419kB 5.8MB/s eta 0:00:01 5% |█▉ | 450kB 2.2MB/s eta 0:00:04 Get:31 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 ghostscript amd64 9.26~dfsg+0-0ubuntu0.18.04.3 [51.8 kB] Get:92 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libbdplus0 amd64 0.1.2-2 [46.6 kB] 76% [43 libpython3.6-dev 30.5 MB/44.8 MB 68%] 4611 kB/s 4s Downloading https://files.pythonhosted.org/packages/8a/48/a76be51647d0eb9f10e2a4511bf3ffb8cc1e6b14e9e4fab46173aa79f981/termcolor-1 Found existing installation: certifi 2018.1.18 Unpacking libopus0:amd64 (1.1.2-1ubuntu1) ... sh && /bin/bash lxc-install.sh" 38% |████████████▌ | 3.2MB 5.9MB/s eta 0:00:01 Receiving objects: 32% (1783/5570), 4.67 MiB | 3.10 MiB/s Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/actions.min.js' 31% [47 libcairo2 2613 B/580 kB 0%] bms@pingu : ~ $ lxc launch ubuntu: paperless Selecting previously unselected package libgs9-common. Downloading https://files.pythonhosted.org/packages/1b/e2/ffb8c1b574f972cf4183b0aac8f16b57f1e3bbe876b31555b107ea3fd009/ipython-7.1 Get:38 http://archive.ubuntu.com/ubuntu bionic/main amd64 libalgorithm-diff-perl all 1.19.03-1 [47.6 kB] Downloading https://files.pythonhosted.org/packages/79/b1/eace304ef66bd7d3d8b2f78cc374b73ca03bc53664d78151e9df3b3996cc/more_iterto Collecting packaging==18.0 (from -r requirements.txt (line 39)) Setting up libvorbisfile3:amd64 (1.3.5-4.2) ... .1.0.tar.gz Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/docs/css/highlight.css' Unpacking libpaper1:amd64 (1.1.24+nmu5ubuntu1) ... Collecting python-levenshtein>=0.12; extra == "speedup" (from fuzzywuzzy[speedup]==0.15.0->-r requirements.txt (line 27)) Get:8 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 gcc-7-base amd64 7.3.0-27ubuntu1~18.04 [18.9 kB] 34% [Waiting for headers] Get:7 http://security.ubuntu.com/ubuntu bionic-security/multiverse Sources [1336 B] The following additional packages will be installed: Password: Copying '/usr/local/lib/python3.6/dist-packages/django_extensions/static/django_extensions/css/jquery.autocomplete.css' 19% [17 liblsan0 2613 B/132 kB 2%] Collecting pyocr==0.5.3 (from -r requirements.txt (line 52)) Preparing to unpack .../093-libdjvulibre-text_3.5.27.1-8_all.deb ... Get:84 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libbluray2 amd64 1:1.0.2-3 [141 kB] 20% [19 libubsan0 2613 B/126 kB 2%] 36% [61 libharfbuzz0b 2613 B/232 kB 1%] 52% |████████████████▉ | 3.7MB 5.9MB/s eta 0:00:01 Selecting previously unselected package libnspr4:amd64. Unpacking ghostscript (9.26~dfsg+0-0ubuntu0.18.04.3) ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/SelectFilter2.js' Selecting previously unselected package fonts-noto-mono. Setting up libgomp1:amd64 (8.2.0-1ubuntu2~18.04) ... Setting up fontconfig-config (2.12.6-0ubuntu2) ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/icon-clock.svg' 21% [23 libgcc-7-dev 2611 B/2380 kB 0%] Unpacking binutils (2.30-21ubuntu1~18.04) ... Unpacking imagemagick (8:6.9.7.4+dfsg-16ubuntu6.4) ... Unpacking python3-dev (3.6.7-1~18.04) ... Setting up gcc-7 (7.3.0-27ubuntu1~18.04) ... 100% |████████████████████████████████| 849kB 1.1MB/s 11% [19 poppler-data 2611 B/1479 kB 0%] 82% |██████████████████████████▌ | 6.7MB 5.9MB/s eta 0:00:01 python3-secretstorage python3-setuptools python3-wheel python3-xdg python3.6-dev python3.6-venv Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/zh-TW.js' 98% |███████████████████████████████▌| 1.9MB 6.0MB/s eta 0:00:01 Unpacking libjbig2dec0:amd64 (0.13-6) ... 43% [Waiting for headers] 3755 kB/s 10s Downloading https://files.pythonhosted.org/packages/8f/67/a1aedccc143597eea90e79134d14c7364e90174d31443e14674919716d87/filemagic-1 87 packages can be updated. share-0.7.5 pillow-5.3.0 pluggy-0.8.0 prompt-toolkit-2.0.7 psycopg2-2.7.6.1 ptyprocess-0.6.0 py-1.7.0 pycodestyle-2.4.0 pygments-2.2 Selecting previously unselected package libnetpbm10. Unpacking libcupsfilters1:amd64 (1.20.2-0ubuntu3) ... 77% |████████████████████████▉ | 6.3MB 5.9MB/s eta 0:00:01 Unpacking python3-wheel (0.30.0-0.2) ... 95% [Waiting for headers] 4944 kB/s 0s Get:82 http://archive.ubuntu.com/ubuntu bionic/universe amd64 libzvbi0 amd64 0.2.35-13 [235 kB] Setting up libxshmfence1:amd64 (1.3-1) ... Reading package lists... 46% Selecting previously unselected package libapache2-mod-wsgi-py3. 98% [Working] 4630 kB/s 0s 97% [21 Translation-en store 0 B] 4229 kB/s 0s 5% [Waiting for headers] Setting up libpangocairo-1.0-0:amd64 (1.40.14-1ubuntu0.1) ... Setting up libxfixes3:amd64 (1:5.0.3-1) ... 7.6.1-cp36-cp36m-manylinux1_x86_64.whl (2.7MB) 32% |██████████▎ | 296kB 5.6MB/s eta 0:00:01 5% [Waiting for headers] Applying documents.0004_auto_20160114_1844... OK Connection to pingu.lan closed. Downloading https://files.pythonhosted.org/packages/e5/c6/ce130213489969aa58610042dff1d908c25c731c9575af6935c2dfad03aa/pycodestyle * Canonical Livepatch is available for installation. Setting up libpython3-dev:amd64 (3.6.7-1~18.04) ... => There is 1 zombie process. Unpacking libzvbi0:amd64 (0.2.35-13) ... Selecting previously unselected package libpoppler-dev:amd64. Enabling site paperless. Get:122 http://archive.ubuntu.com/ubuntu bionic/main amd64 libxcb-dri2-0 amd64 1.13-1 [6928 B] Setting up libtesseract4 (4.00~git2288-10f4998a-2) ... Collecting filemagic==1.6 (from -r requirements.txt (line 26)) 31% |██████████▏ | 317kB 5.9MB/s eta 0:00:01 Applying documents.0002_auto_20151226_1316... OK 7% [14 libtiff5 2613 B/152 kB 2%] 14% |████▌ | 71kB 4.7MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/en.js' Get:11 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 cpp-7 amd64 7.3.0-27ubuntu1~18.04 [6738 kB] Created symlink /etc/systemd/system/multi-user.target.wants/apache-htcacheclean.service → /lib/systemd/system/apache-htcacheclean.se Downloading https://files.pythonhosted.org/packages/52/69/3c2fbdc3702358c5b34ee25e387b24838597ef099761fc9a42c166796e8f/sphinxcontr 34% [55 libgdk-pixbuf2.0-0 4061 B/165 kB 2%] Setting up apache2-data (2.4.29-1ubuntu4.5) ... 93% [Waiting for headers] 4611 kB/s 0s Collecting django-cors-headers==2.4.0 (from -r requirements.txt (line 14)) Preparing to unpack .../05-apache2-bin_2.4.29-1ubuntu4.5_amd64.deb ... 51% [Waiting for headers] 3755 kB/s 8s Get:1 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 binutils-common amd64 2.30-21ubuntu1~18.04 [193 kB] Get:7 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 apache2-utils amd64 2.4.29-1ubuntu4.5 [83.3 kB] Downloading https://files.pythonhosted.org/packages/d9/eb/6d13451769976f79c9043db562da22302a2c3d96e5d6427e8517fec9d2e5/Faker-0.9.2 99% |███████████████████████████████▊| 501kB 5.8MB/s eta 0:00:01 Unpacking binutils-x86-64-linux-gnu (2.30-21ubuntu1~18.04) ... Downloading https://files.pythonhosted.org/packages/02/ee/b6e02dc6529e82b75bb06823ff7d005b141037cb1416b10c6f00fc419dca/Pygments-2. Get:102 http://archive.ubuntu.com/ubuntu bionic/main amd64 libgif7 amd64 5.1.4-2 [30.6 kB] Unpacking python3-xdg (0.25-4ubuntu1) ... Selecting previously unselected package libavahi-common3:amd64. Selecting previously unselected package libopenexr22:amd64. Setting up libaprutil1:amd64 (1.6.1-2) ... Unpacking libdrm-amdgpu1:amd64 (2.4.91-2) ... Selecting previously unselected package libx264-152:amd64. * Support: https://ubuntu.com/advantage 10% |███▌ | 778kB 6.0MB/s eta 0:00:02 tesseract-ocr-osd unpaper va-driver-all vdpau-driver-all Setting up libopenexr22:amd64 (2.2.0-11.1ubuntu1) ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/fonts/LICENSE.txt' Selecting previously unselected package libthai0:amd64. Selecting previously unselected package fonts-dejavu-core. 26% |████████▌ | 204kB 5.9MB/s eta 0:00:01 Unpacking libquadmath0:amd64 (8.2.0-1ubuntu2~18.04) ... 100% |████████████████████████████████| 225kB 2.8MB/s Collecting snowballstemmer==1.2.1 (from -r requirements.txt (line 69)) 54% [95 libdjvulibre21 4061 B/559 kB 1%] 3755 kB/s 8s 95% [47 python3-crypto 2613 B/244 kB 1%] 4611 kB/s 0s Applying documents.0012_auto_20160305_0040... OK https://ubuntu.com/livepatch Get:42 http://archive.ubuntu.com/ubuntu bionic/main amd64 libfile-fcntllock-perl amd64 0.22-3build2 [33.2 kB] Get:14 http://security.ubuntu.com/ubuntu bionic-security/universe Translation-en [64.2 kB] Unpacking libatomic1:amd64 (8.2.0-1ubuntu2~18.04) ... 34% |███████████ | 2.5MB 5.8MB/s eta 0:00:01 Preparing to unpack .../132-python3-pil_5.1.0-1_amd64.deb ... Downloading https://files.pythonhosted.org/packages/91/4f/f52682bfaa960445af93143df5ea0a98854f1a7829ff3e377932426bf48c/coveralls-1 Get:36 http://archive.ubuntu.com/ubuntu bionic/main amd64 libfakeroot amd64 1.22-2ubuntu1 [25.9 kB] 77% [8 apache2-data 2613 B/160 kB 2%] Preparing to unpack .../072-libvorbisenc2_1.3.5-4.2_amd64.deb ... Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/docs/css/jquery.json-view.min.css' Running setup.py bdist_wheel for python-levenshtein ... \ 68% [43 libpython3.6-dev 21.9 MB/44.8 MB 49%] 4611 kB/s 6s Running setup.py bdist_wheel for tzlocal ... \ Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/pl.js' Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/ms.js' 26% |████████▍ | 808kB 6.0MB/s eta 0:00:01 24% [Waiting for headers] 98% [53 python3-pip 4061 B/114 kB 4%] 4944 kB/s 0s Setting up manpages-dev (4.15-1) ... 32% |██████████▍ | 1.0MB 5.8MB/s eta 0:00:01 Copying '/home/paperless/paperless/src/documents/static/paperless.css' Preparing to unpack .../106-libwmf0.2-7_0.2.8.4-12_amd64.deb ... 18% [Working] Preparing to unpack .../54-python3.6-venv_3.6.7-1~18.04_amd64.deb ... 73% |███████████████████████▌ | 5.2MB 6.0MB/s eta 0:00:01 Selecting previously unselected package libijs-0.35:amd64. Preparing to unpack .../29-libdpkg-perl_1.19.0.5ubuntu2.1_all.deb ... Setting up python3-dev (3.6.7-1~18.04) ... Unpacking fonts-droid-fallback (1:6.0.1r16-1.1) ... Collecting coveralls==1.5.1 (from -r requirements.txt (line 11)) Unpacking libmemcachedutil2:amd64 (1.0.18-4.2) ... Get:67 http://archive.ubuntu.com/ubuntu bionic/main amd64 libspeex1 amd64 1.2~rc1.2-1ubuntu2 [52.1 kB] Preparing to unpack .../085-libgme0_0.6.2-1_amd64.deb ... Downloading https://files.pythonhosted.org/packages/a2/55/8f8cab2afd404cf578136ef2cc5dfb50baa1761b68c9da1fb1e4eed343c9/docopt-0.6. 44% |██████████████▏ | 3.2MB 6.0MB/s eta 0:00:01 Reading package lists... 8% Get:13 http://archive.ubuntu.com/ubuntu bionic/main amd64 libltdl7 amd64 2.4.6-2 [38.8 kB] Downloading https://files.pythonhosted.org/packages/1c/e7/017c262070af41fe251401cb0d0e1b7c38f656da634cd0c15604f1f30864/pluggy-0.8. Selecting previously unselected package libubsan0:amd64. Receiving objects: 54% (3008/5570), 4.67 MiB | 3.10 MiB/s bms@FortBoyard:~$ exit 97% |███████████████████████████████▎| 7.9MB 6.0MB/s eta 0:00:01 Preparing to unpack .../03-binutils_2.30-21ubuntu1~18.04_amd64.deb ... Get:133 http://archive.ubuntu.com/ubuntu bionic/main amd64 python3-pil amd64 5.1.0-1 [328 kB] Get:23 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libavahi-client3 amd64 0.7-3.1ubuntu1.1 [25.3 kB] Setting up mesa-vdpau-drivers:amd64 (18.0.5-0ubuntu0~18.04.1) ... 1-py2.py3-none-any.whl (3.1MB) Preparing to unpack .../053-libgdk-pixbuf2.0-common_2.36.11-2_all.deb ... Get:37 http://archive.ubuntu.com/ubuntu bionic/main amd64 fakeroot amd64 1.22-2ubuntu1 [62.3 kB] 51% |████████████████▍ | 3.6MB 6.0MB/s eta 0:00:01 96% |███████████████████████████████ | 102kB 6.9MB/s eta 0:00:01 100% |████████████████████████████████| 552kB 1.5MB/s 95% |██████████████████████████████▋ | 61kB 5.2MB/s eta 0:00:01 59% |███████████████████▏ | 4.9MB 6.0MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/fonts/README.txt' Applying documents.0020_document_added... OK 100% |████████████████████████████████| 7.1MB 158kB/s 76% [105 libllvm6.0 14.4 MB/14.5 MB 99%] 3755 kB/s 4s Unpacking libwebpdemux2:amd64 (0.6.1-2) ... Unpacking libstdc++-7-dev:amd64 (7.3.0-27ubuntu1~18.04) ... Setting up libbdplus0:amd64 (0.1.2-2) ... Preparing to unpack .../055-libthai-data_0.1.27-2_all.deb ... Unpacking libx264-152:amd64 (2:0.152.2854+gite9a5903-2) ... Unpacking libaprutil1-dbd-sqlite3:amd64 (1.6.1-2) ... Selecting previously unselected package unpaper. HTTP request sent, awaiting response... 200 OK Get:135 http://archive.ubuntu.com/ubuntu bionic/universe amd64 tesseract-ocr-eng all 4.00~git24-0e00fe6-1.2 [1588 kB] Applying documents.0011_auto_20160303_1929... OK Preparing to unpack .../043-libxcb-render0_1.13-1_amd64.deb ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/hr.js' 51% [Working] 3755 kB/s 8s Setting up libxrender1:amd64 (1:0.9.10-1) ... Preparing to unpack .../094-libdjvulibre21_3.5.27.1-8_amd64.deb ... Enabling module deflate. 47% [83 libavcodec57 2878 kB/4592 kB 63%] 3755 kB/s 9s Reading state information... Done Unpacking libxvidcore4:amd64 (2:1.3.5-1) ... Setting up libcupsimage2:amd64 (2.2.7-1ubuntu2.3) ... 57% |██████████████████▍ | 4.7MB 6.0MB/s eta 0:00:01 100% |████████████████████████████████| 81kB 3.5MB/s Preparing to unpack .../00-libapr1_1.6.3-2_amd64.deb ... Preparing to unpack .../075-libwebp6_0.6.1-2_amd64.deb ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/bg.js' Setting up python3-distutils (3.6.7-1~18.04) ... 14% [Working] 5% [Waiting for headers] Running setup.py bdist_wheel for backcall ... - Unpacking libtheora0:amd64 (1.1.1+dfsg.1-14) ... Get:116 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libpoppler-cpp-dev amd64 0.62.0-2ubuntu2.5 [8680 B] Preparing to unpack .../010-liblcms2-2_2.9-1ubuntu0.1_amd64.deb ... 99% |███████████████████████████████▉| 215kB 5.8MB/s eta 0:00:01 70% |██████████████████████▋ | 5.0MB 6.0MB/s eta 0:00:01 25% |████████▏ | 501kB 5.9MB/s eta 0:00:01 68% |█████████████████████▊ | 5.5MB 5.9MB/s eta 0:00:01 60% |███████████████████▎ | 1.6MB 5.8MB/s eta 0:00:01 Preparing to unpack .../18-libubsan0_7.3.0-27ubuntu1~18.04_amd64.deb ... Unpacking fontconfig (2.12.6-0ubuntu2) ... 68% |█████████████████████▉ | 522kB 6.0MB/s eta 0:00:01 libc6-dev libcc1-0 libcilkrts5 libdpkg-perl libexpat1-dev libfakeroot libfile-fcntllock-perl libgcc-7-dev libisl19 libitm1 Unpacking libdrm-nouveau2:amd64 (2.4.91-2) ... Running setup.py bdist_wheel for docopt ... - Copying '/usr/local/lib/python3.6/dist-packages/django_extensions/static/django_extensions/js/jquery.ajaxQueue.js' Get:50 http://archive.ubuntu.com/ubuntu bionic/main amd64 libmp3lame0 amd64 3.100-2 [136 kB] Selecting previously unselected package libcups2:amd64. 54% [Waiting for headers] 3755 kB/s 8s Get:29 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libgs9-common all 9.26~dfsg+0-0ubuntu0.18.04.3 [5094 kB] Preparing to unpack .../030-ghostscript_9.26~dfsg+0-0ubuntu0.18.04.3_amd64.deb ... Running setup.py bdist_wheel for pytest-env ... - Stored in directory: /root/.cache/pip/wheels/2e/0e/8e/c4625de9d6f723017996dd718d5fabe1c7bb1fbce7389d3bf6 Get:120 http://archive.ubuntu.com/ubuntu bionic/main amd64 libwebpdemux2 amd64 0.6.1-2 [9472 B] libbluray-bdj firmware-crystalhd cups-common libfftw3-bin libfftw3-dev liblcms2-utils inkscape libjxr-tools opus-tools 55% |█████████████████▊ | 112kB 6.5MB/s eta 0:00:01 Preparing to unpack .../084-libchromaprint1_1.4.3-1_amd64.deb ... Selecting previously unselected package apache2-utils. 89% [130 netpbm 2612 B/1017 kB 0%] 4630 kB/s 1s Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.16.133 Applying documents.0016_auto_20170325_1558... OK 13% |████▏ | 409kB 5.5MB/s eta 0:00:01 Stored in directory: /root/.cache/pip/wheels/9b/04/dd/7daf4150b6d9b12949298737de9431a324d4b797ffd63f526e Preparing to unpack .../53-python3-setuptools_39.0.1-2_all.deb ... Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/LICENSE-SELECT2.md' Downloading https://files.pythonhosted.org/packages/4b/2a/0276479a4b3caeb8a8c1af2f8e4355746a97fab05a372e4a2c6a6b876165/idna-2.7-py Selecting previously unselected package i965-va-driver:amd64. Get:58 http://archive.ubuntu.com/ubuntu bionic/main amd64 python3-xdg all 0.25-4ubuntu1 [31.4 kB] Preparing to unpack .../064-libshine3_3.1.1-1_amd64.deb ... Enabling module status. 67% [Waiting for headers] Downloading https://files.pythonhosted.org/packages/56/9d/1d02dd80bc4cd955f98980f28c5ee2200e1209292d5f9e9cc8d030d18655/certifi-201 Unpacking libjpeg-turbo8:amd64 (1.5.2-0ubuntu5.18.04.1) ... Unpacking vdpau-driver-all:amd64 (1.1.1-3ubuntu1) ... 98% [Waiting for headers] 4630 kB/s 0s Selecting previously unselected package poppler-data. 93% |██████████████████████████████ | 716kB 5.9MB/s eta 0:00:01 21% [22 libquadmath0 2613 B/133 kB 2%] Selecting previously unselected package libaprutil1:amd64. Get:21 http://archive.ubuntu.com/ubuntu bionic/multiverse Translation-en [108 kB] 82% |██████████████████████████▎ | 5.8MB 5.9MB/s eta 0:00:01 56% |██████████████████▎ | 4.1MB 6.0MB/s eta 0:00:01 Operations to perform: Get:129 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 mesa-vdpau-drivers amd64 18.0.5-0ubuntu0~18.04.1 [1902 kB] Get:71 http://archive.ubuntu.com/ubuntu bionic/main amd64 libtwolame0 amd64 0.3.13-3 [46.7 kB] Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/css/vendor/select2/LICENSE-SELECT2.md' Selecting previously unselected package libjbig2dec0:amd64. Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/fa.js' Unpacking libharfbuzz0b:amd64 (1.7.2-1ubuntu1) ... Get:53 http://archive.ubuntu.com/ubuntu bionic/main amd64 libcroco3 amd64 0.6.12-2 [81.3 kB] erage-4.5.1 coveralls-1.5.1 dateparser-0.7.0 decorator-4.3.0 django-2.0.9 django-cors-headers-2.4.0 django-crispy-forms-1.7.2 django 100% |████████████████████████████████| 2.0MB 515kB/s Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/prepopulate_init.js' Get:126 http://archive.ubuntu.com/ubuntu bionic/main amd64 libxcb-xfixes0 amd64 1.13-1 [9340 B] Preparing to unpack .../49-python3-secretstorage_2.3.1-2_all.deb ... Receiving objects: 1% (56/5570) Setting up libdrm-radeon1:amd64 (2.4.91-2) ... 98% [25 Sources store 0 B] 4229 kB/s 0s 30% [Waiting for headers] Get:12 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 cpp amd64 4:7.3.0-3ubuntu2.1 [27.6 kB] 81% |██████████████████████████▏ | 1.6MB 5.8MB/s eta 0:00:01 Get:128 http://archive.ubuntu.com/ubuntu bionic-updates/universe amd64 mesa-va-drivers amd64 18.0.5-0ubuntu0~18.04.1 [1778 kB] libva-x11-2 libva2 libvdpau1 libvorbis0a libvorbisenc2 libvorbisfile3 libvpx5 libwavpack1 libwebp6 libwebpdemux2 libwebpmux3 Receiving objects: 21% (1170/5570), 676.01 KiB | 1.30 MiB/s libopus0 libpango-1.0-0 libpangocairo-1.0-0 libpangoft2-1.0-0 libpaper-utils libpaper1 libpciaccess0 libpixman-1-0 Preparing to unpack .../40-libexpat1-dev_2.2.5-3_amd64.deb ... Selecting previously unselected package libisl19:amd64. Setting up libzvbi-common (0.2.35-13) ... 30% |█████████▊ | 2.2MB 5.9MB/s eta 0:00:01 Preparing to unpack .../122-libxcb-dri3-0_1.13-1_amd64.deb ... Preparing to unpack .../042-libpixman-1-0_0.34.0-2_amd64.deb ... Collecting pytest-django==3.4.3 (from -r requirements.txt (line 55)) libavahi-common3 libavcodec57 libavformat57 libavutil55 libbdplus0 libbluray2 libcairo2 libchromaprint1 libcroco3 libcrystalhd3 Get:19 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libubsan0 amd64 7.3.0-27ubuntu1~18.04 [126 kB] Preparing to unpack .../119-libwebpdemux2_0.6.1-2_amd64.deb ... Get:74 http://archive.ubuntu.com/ubuntu bionic/main amd64 libvpx5 amd64 1.7.0-3 [798 kB] Downloading https://files.pythonhosted.org/packages/d4/6c/8a935e2c7b54a37714656d753e4187ee0631988184ed50c0cf6476858566/snowballste 19% |██████▏ | 1.6MB 4.6MB/s eta 0:00:02 Receiving objects: 20% (1114/5570) 43% [Working] 3755 kB/s 10s 0.10-py3-none-any.whl Adding user `paperless' ... ple-1.1.8.tar.gz Preparing to unpack .../110-libnss3_2%3a3.35-2ubuntu2.1_amd64.deb ... Unpacking make (4.1-9.1ubuntu1) ... 97% |███████████████████████████████▍| 143kB 5.9MB/s eta 0:00:01 Setting up libwebp6:amd64 (0.6.1-2) ... 37% [Waiting for headers] Collecting pytest-forked==0.2 (from -r requirements.txt (line 57)) Unpacking libgsm1:amd64 (1.0.13-4build1) ... 7% [Waiting for headers] Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/hu.js' Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/admin/RelatedObjectLookups.js' Unpacking liblsan0:amd64 (8.2.0-1ubuntu2~18.04) ... Get:73 http://archive.ubuntu.com/ubuntu bionic/main amd64 libvorbisenc2 amd64 1.3.5-4.2 [70.7 kB] Adding new group `ftpupload' (1001) ... Setting up ssl-cert (1.0.39) ... Setting up imagemagick-6-common (8:6.9.7.4+dfsg-16ubuntu6.4) ... 54% |█████████████████▋ | 4.4MB 5.3MB/s eta 0:00:01 Unpacking g++ (4:7.3.0-3ubuntu2.1) ... Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/fonts/fontawesome-webfont.ttf' 23% |███████▌ | 1.7MB 6.0MB/s eta 0:00:01 --2019-01-20 19:50:05-- https://raw.githubusercontent.com/bmsleight/paperless/master/scripts/lxc/lxc-install.sh Unpacking libsensors4:amd64 (1:3.4.0-4) ... 45% [Working] 4081 kB/s 12s Receiving objects: 31% (1727/5570), 2.15 MiB | 2.14 MiB/s 100% |████████████████████████████████| 1.0MB 946kB/s 49% |████████████████ | 3.6MB 6.0MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/css/autocomplete.css' 28% [32 gsfonts 2875 kB/3120 kB 92%] 95% |██████████████████████████████▊ | 71kB 5.0MB/s eta 0:00:01 Downloading https://files.pythonhosted.org/packages/6a/8b/8517167a0adc45ce94d0873efb9487dd4cdeff7e10f96e837ad3d58f5837/django_filt Preparing to unpack .../045-libxrender1_1%3a0.9.10-1_amd64.deb ... Setting up libgdk-pixbuf2.0-common (2.36.11-2) ... Unpacking fonts-dejavu-core (2.37-1) ... Downloading https://files.pythonhosted.org/packages/59/59/4bc44158a767a6d66de18c4136c8aa90491d56cc951c10b74dd1e13213c9/langdetect- Preparing to unpack .../059-libgraphite2-3_1.3.11-2_amd64.deb ... 8% |██▋ | 655kB 2.3MB/s eta 0:00:04 Creating config file /etc/papersize with new version Get:14 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libitm1 amd64 8.2.0-1ubuntu2~18.04 [28.1 kB] Selecting previously unselected package libxvidcore4:amd64. Selecting previously unselected package libzvbi-common. Unpacking tesseract-ocr (4.00~git2288-10f4998a-2) ... 100% |████████████████████████████████| 61kB 5.0MB/s Unpacking libfile-fcntllock-perl (0.22-3build2) ... Preparing to unpack .../121-libxcb-dri2-0_1.13-1_amd64.deb ... Selecting previously unselected package libdrm-radeon1:amd64. Setting up libsensors4:amd64 (1:3.4.0-4) ... Running setup.py bdist_wheel for langdetect ... done Preparing to unpack .../017-libogg0_1.3.2-1_amd64.deb ... s-1.2.1-py2.py3-none-any.whl 50% [87 libmpg123-0 2613 B/125 kB 2%] 3755 kB/s 8s Installing from a newer Wheel-Version (1.1) Setting up libexpat1-dev:amd64 (2.2.5-3) ... Unpacking python3-distutils (3.6.7-1~18.04) ... Unpacking libpoppler-cpp0v5:amd64 (0.62.0-2ubuntu2.5) ... Selecting previously unselected package libx265-146:amd64. Selecting previously unselected package libmp3lame0:amd64. 14% [Waiting for headers] Preparing to unpack .../116-librsvg2-common_2.40.20-2_amd64.deb ... Selecting previously unselected package manpages-dev. Selecting previously unselected package libpango-1.0-0:amd64. libwmf0.2-7 libx11-xcb1 libx264-152 libx265-146 libxcb-dri2-0 libxcb-dri3-0 libxcb-present0 libxcb-render0 libxcb-shm0 100% |████████████████████████████████| 112kB 4.1MB/s 99% |████████████████████████████████| 337kB 6.1MB/s eta 0:00:01 86% [Waiting for headers] Created symlink /etc/systemd/system/multi-user.target.wants/apache2.service → /lib/systemd/system/apache2.service. 43% |██████████████ | 3.5MB 6.0MB/s eta 0:00:01 Selecting previously unselected package libopenjp2-7:amd64. /usr/local/lib/python3.6/dist-packages/psycopg2/__init__.py:144: UserWarning: The psycopg2 wheel package will be renamed from releas 71% [Working] Selecting previously unselected package libwavpack1:amd64. Preparing to unpack .../01-libaprutil1_1.6.1-2_amd64.deb ... The following NEW packages will be installed: Selecting previously unselected package linux-libc-dev:amd64. update-alternatives: using /usr/bin/display-im6.q16 to provide /usr/bin/display-im6 (display-im6) in auto mode 31% [Waiting for headers] 99% |████████████████████████████████| 2.0MB 6.2MB/s eta 0:00:01 Get:20 http://archive.ubuntu.com/ubuntu bionic/multiverse amd64 Packages [151 kB] libavcodec57 libavformat57 libavutil55 libbdplus0 libbluray2 libcairo2 libchromaprint1 libcroco3 libcrystalhd3 libcups2 97% [Waiting for headers] update-alternatives: using /usr/bin/conjure-im6.q16 to provide /usr/bin/conjure-im6 (conjure-im6) in auto mode 98% [137 tesseract-ocr 2613 B/218 kB 1%] 4630 kB/s 0s Created symlink /etc/systemd/system/multi-user.target.wants/paperless-consumer.service → /etc/systemd/system/paperless-consumer.serv After this operation, 8347 kB of additional disk space will be used. thon-levenshtein, fuzzywuzzy, gunicorn, imagesize, inotify-simple, ipython-genutils, pickleshare, ptyprocess, pexpect, parso, jedi, 28% [14 Translation-en store 0 B] [10 Sources 2419 kB/9051 kB 27%] [Waiting for headers] Collecting django-filter==2.0.0 (from -r requirements.txt (line 17)) Selecting previously unselected package libltdl7:amd64. Preparing to unpack .../047-libcrystalhd3_1%3a0.0~git20110715.fdd2f19-12_amd64.deb ... Collecting psycopg2==2.7.6.1 (from -r requirements.txt (line 46)) Selecting previously unselected package python3-secretstorage. 15% |█████ | 307kB 5.9MB/s eta 0:00:01 Setting up libxcb-sync1:amd64 (1.13-1) ... 35% [59 libpango-1.0-0 2613 B/153 kB 2%] Preparing to unpack .../009-libjpeg8_8c-2ubuntu8_amd64.deb ... 83% [Waiting for headers] 4630 kB/s 2s Selecting previously unselected package mesa-vdpau-drivers:amd64. 43% [80 libxvidcore4 2613 B/200 kB 1%] 3755 kB/s 10s 77% [Waiting for headers] Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/select2.full.min.js' 86% |███████████████████████████▌ | 2.3MB 5.9MB/s eta 0:00:01 Setting up libc-dev-bin (2.27-3ubuntu1) ... 87% [Waiting for headers] 4630 kB/s 2s -0.7.5-py2.py3-none-any.whl Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/fonts/fontawesome-webfont.eot' Collecting apipkg==1.5 (from -r requirements.txt (line 3)) Applying auth.0005_alter_user_last_login_null... OK libvdpau1 libvorbis0a libvorbisenc2 libvorbisfile3 libvpx5 libwavpack1 libwebp6 libwebpdemux2 libwebpmux3 libwmf0.2-7 97% [20 Packages store 0 B] 4229 kB/s 0s Get:20 http://archive.ubuntu.com/ubuntu bionic-updates/main amd64 libcilkrts5 amd64 7.3.0-27ubuntu1~18.04 [42.5 kB] Setting up libmagickcore-6.q16-3:amd64 (8:6.9.7.4+dfsg-16ubuntu6.4) ... 36% [Waiting for headers] Adding new user `ftpupload' (1001) with group `ftpupload' ... Selecting previously unselected package libxcb-present0:amd64. 98% [Working] 4944 kB/s 0s Unpacking libc-dev-bin (2.27-3ubuntu1) ... 76% |████████████████████████▌ | 6.2MB 6.0MB/s eta 0:00:01 96% [18 Packages store 0 B] [29 Translation-en 4135 B/175 kB 2%] 99% |████████████████████████████████| 112kB 6.5MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/vendor/select2/i18n/el.js' 6% |██ | 501kB 2.7MB/s eta 0:00:03 Unpacking libgme0:amd64 (0.6.2-1) ... Copying '/usr/local/lib/python3.6/dist-packages/rest_framework/static/rest_framework/img/glyphicons-halflings.png' Selecting previously unselected package libmagickcore-6.q16-3:amd64. -py2.py3-none-any.whl (177kB) Resolving deltas: 75% (2688/3560) Preparing to unpack .../097-libdrm-intel1_2.4.91-2_amd64.deb ... Preparing to unpack .../082-libavcodec57_7%3a3.4.4-0ubuntu0.18.04.1_amd64.deb ... 11% [Waiting for headers] Get:35 http://archive.ubuntu.com/ubuntu bionic/main amd64 dh-python all 3.20180325ubuntu2 [89.2 kB] Setting up libgcc-7-dev:amd64 (7.3.0-27ubuntu1~18.04) ... liblsan0 libmpc3 libmpx2 libpython3-dev libpython3.6-dev libquadmath0 libstdc++-7-dev libtsan0 libubsan0 linux-libc-dev make Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/js/collapse.min.js' .1.0-py2.py3-none-any.whl 26% |████████▍ | 2.1MB 5.1MB/s eta 0:00:02 0% [1 binutils-common 2613 B/193 kB 1%] Collecting prompt-toolkit==2.0.7 (from -r requirements.txt (line 47)) Setting up libharfbuzz0b:amd64 (1.7.2-1ubuntu1) ... 70% |██████████████████████▊ | 1.4MB 5.9MB/s eta 0:00:01 11% |███▊ | 307kB 5.8MB/s eta 0:00:01 Unpacking libvorbis0a:amd64 (1.3.5-4.2) ... Unpacking libchromaprint1:amd64 (1.4.3-1) ... Unpacking python3-crypto (2.6.1-8ubuntu2) ... Downloading https://files.pythonhosted.org/packages/bc/bb/a24838832ba35baf52f32ab1a49b906b5f82fb7c76b2f6a7e35e140bac30/decorator-4 30% [Working] 92% |█████████████████████████████▊ | 7.5MB 5.8MB/s eta 0:00:01 Selecting previously unselected package cpp. 38% [70 libtheora0 2613 B/170 kB 2%] Unpacking libpoppler-cpp-dev:amd64 (0.62.0-2ubuntu2.5) ... 15% |█████ | 1.3MB 3.3MB/s eta 0:00:03 91% [133 python3-pil 2613 B/328 kB 1%] 4630 kB/s 1s Stored in directory: /root/.cache/pip/wheels/de/c2/93/660fd5f7559049268ad2dc6d81c4e39e9e36518766eaf7e342 Running setup.py bdist_wheel for pytest-env ... done 20% |██████▋ | 1.5MB 5.9MB/s eta 0:00:01 Copying '/usr/local/lib/python3.6/dist-packages/django/contrib/admin/static/admin/img/LICENSE' 100% |████████████████████████████████| 204kB 2.8MB/s Get:4 http://archive.ubuntu.com/ubuntu bionic/universe amd64 proftpd-doc all 1.3.5e-1build1 [1165 kB] 65% |█████████████████████ | 4.6MB 5.9MB/s eta 0:00:01 Saving to: ‘lxc-install.sh’ Get:47 http://archive.ubuntu.com/ubuntu bionic/main amd64 libcairo2 amd64 1.15.10-2 [580 kB] System load: 0.0 Users logged in: 1
-
\ No newline at end of file
diff --git a/docs/_static/recommended_workflow.png b/docs/_static/recommended_workflow.png
new file mode 100644
index 000000000..bbab0bb34
Binary files /dev/null and b/docs/_static/recommended_workflow.png differ
diff --git a/docs/_static/screenshots/correspondents.png b/docs/_static/screenshots/correspondents.png
new file mode 100644
index 000000000..269543b26
Binary files /dev/null and b/docs/_static/screenshots/correspondents.png differ
diff --git a/docs/_static/screenshots/dashboard.png b/docs/_static/screenshots/dashboard.png
new file mode 100644
index 000000000..3a3494e72
Binary files /dev/null and b/docs/_static/screenshots/dashboard.png differ
diff --git a/docs/_static/screenshots/documents-filter.png b/docs/_static/screenshots/documents-filter.png
new file mode 100644
index 000000000..feb0f58d5
Binary files /dev/null and b/docs/_static/screenshots/documents-filter.png differ
diff --git a/docs/_static/screenshots/documents-largecards.png b/docs/_static/screenshots/documents-largecards.png
new file mode 100644
index 000000000..d65c4a0d7
Binary files /dev/null and b/docs/_static/screenshots/documents-largecards.png differ
diff --git a/docs/_static/screenshots/documents-smallcards.png b/docs/_static/screenshots/documents-smallcards.png
new file mode 100644
index 000000000..77de7a04d
Binary files /dev/null and b/docs/_static/screenshots/documents-smallcards.png differ
diff --git a/docs/_static/screenshots/documents-table.png b/docs/_static/screenshots/documents-table.png
new file mode 100644
index 000000000..a5f2a3d7f
Binary files /dev/null and b/docs/_static/screenshots/documents-table.png differ
diff --git a/docs/_static/screenshots/editing.png b/docs/_static/screenshots/editing.png
new file mode 100644
index 000000000..21a0bcdf2
Binary files /dev/null and b/docs/_static/screenshots/editing.png differ
diff --git a/docs/_static/screenshots/logs.png b/docs/_static/screenshots/logs.png
new file mode 100644
index 000000000..10a0d393a
Binary files /dev/null and b/docs/_static/screenshots/logs.png differ
diff --git a/docs/_static/screenshots/mail-rules-edited.png b/docs/_static/screenshots/mail-rules-edited.png
new file mode 100644
index 000000000..68a170ac3
Binary files /dev/null and b/docs/_static/screenshots/mail-rules-edited.png differ
diff --git a/docs/_static/screenshots/mobile.png b/docs/_static/screenshots/mobile.png
new file mode 100644
index 000000000..1217f06b3
Binary files /dev/null and b/docs/_static/screenshots/mobile.png differ
diff --git a/docs/_static/screenshots/new-tag.png b/docs/_static/screenshots/new-tag.png
new file mode 100644
index 000000000..4ea438530
Binary files /dev/null and b/docs/_static/screenshots/new-tag.png differ
diff --git a/docs/_static/screenshots/search-preview.png b/docs/_static/screenshots/search-preview.png
new file mode 100644
index 000000000..78925ae18
Binary files /dev/null and b/docs/_static/screenshots/search-preview.png differ
diff --git a/docs/_static/screenshots/search-results.png b/docs/_static/screenshots/search-results.png
new file mode 100644
index 000000000..f9db14190
Binary files /dev/null and b/docs/_static/screenshots/search-results.png differ
diff --git a/docs/administration.rst b/docs/administration.rst
new file mode 100644
index 000000000..8885b7322
--- /dev/null
+++ b/docs/administration.rst
@@ -0,0 +1,415 @@
+
+**************
+Administration
+**************
+
+.. _administration-backup:
+
+Making backups
+##############
+
+Multiple options exist for making backups of your paperless instance,
+depending on how you installed paperless.
+
+Before making backups, make sure that paperless is not running.
+
+Options available to any installation of paperless:
+
+* Use the :ref:`document exporter `.
+ The document exporter exports all your documents, thumbnails and
+ metadata to a specific folder. You may import your documents into a
+ fresh instance of paperless again or store your documents in another
+ DMS with this export.
+
+Options available to docker installations:
+
+* Backup the docker volumes. These usually reside within
+ ``/var/lib/docker/volumes`` on the host and you need to be root in order
+ to access them.
+
+ Paperless uses 3 volumes:
+
+ * ``paperless_media``: This is where your documents are stored.
+ * ``paperless_data``: This is where auxillary data is stored. This
+ folder also contains the SQLite database, if you use it.
+ * ``paperless_pgdata``: Exists only if you use PostgreSQL and contains
+ the database.
+
+Options available to bare-metal and non-docker installations:
+
+* Backup the entire paperless folder. This ensures that if your paperless instance
+ crashes at some point or your disk fails, you can simply copy the folder back
+ into place and it works.
+
+ When using PostgreSQL, you'll also have to backup the database.
+
+.. _migrating-restoring:
+
+Restoring
+=========
+
+
+
+
+.. _administration-updating:
+
+Updating paperless
+##################
+
+If a new release of paperless-ng is available, upgrading depends on how you
+installed paperless-ng in the first place. The releases are available at
+`release page `_.
+
+First of all, ensure that paperless is stopped.
+
+.. code:: shell-session
+
+ $ cd /path/to/paperless
+ $ docker-compose down
+
+After that, :ref:`make a backup `.
+
+A. If you used the dockerfiles archive, simply download the files of the new release,
+ adjust the settings in the files (i.e., the path to your consumption directory),
+ and replace your existing docker-compose files. Then start paperless as usual,
+ which will pull the new image, and update your database, if necessary:
+
+ .. code:: shell-session
+
+ $ cd /path/to/paperless
+ $ docker-compose up
+
+ If you see everything working, you can start paperless-ng with "-d" to have it
+ run in the background.
+
+ .. hint::
+
+ The released docker-compose files specify exact versions to be pulled from the hub.
+ This is to ensure that if the docker-compose files should change at some point
+ (i.e., services updates/configured differently), you wont run into trouble due to
+ docker pulling the ``latest`` image and running it in an older environment.
+
+B. If you built the image yourself, grab the new archive and replace your current
+ paperless folder with the new contents.
+
+ After that, make the necessary adjustments to the docker-compose.yml (i.e.,
+ adjust your consumption directory).
+
+ Build and start the new image with:
+
+ .. code:: shell-session
+
+ $ cd /path/to/paperless
+ $ docker-compose build
+ $ docker-compose up
+
+ If you see everything working, you can start paperless-ng with "-d" to have it
+ run in the background.
+
+.. hint::
+
+ You can usually keep your ``docker-compose.env`` file, since this file will
+ never include mandatory configuration options. However, it is worth checking
+ out the new version of this file, since it might have new recommendations
+ on what to configure.
+
+
+Updating paperless without docker
+=================================
+
+After grabbing the new release and unpacking the contents, do the following:
+
+1. Update dependencies. New paperless version may require additional
+ dependencies. The dependencies required are listed in the section about
+ :ref:`bare metal installations `.
+
+2. Update python requirements. If you use Pipenv, this is done with the following steps.
+
+ .. code:: shell-session
+
+ $ pip install --upgrade pipenv
+ $ cd /path/to/paperless
+ $ pipenv clean
+ $ pipenv install
+
+ This creates a new virtual environment (or uses your existing environment)
+ and installs all dependencies into it.
+
+3. Collect static files.
+
+ .. code:: shell-session
+
+ $ cd src
+ $ pipenv run python3 manage.py collectstatic --clear
+
+4. Migrate the database.
+
+ .. code:: shell-session
+
+ $ cd src
+ $ pipenv run python3 manage.py migrate
+
+
+Management utilities
+####################
+
+Paperless comes with some management commands that perform various maintenance
+tasks on your paperless instance. You can invoke these commands either by
+
+.. code:: shell-session
+
+ $ cd /path/to/paperless
+ $ docker-compose run --rm webserver
+
+or
+
+.. code:: shell-session
+
+ $ cd /path/to/paperless/src
+ $ pipenv run python manage.py
+
+depending on whether you use docker or not.
+
+All commands have built-in help, which can be accessed by executing them with
+the argument ``--help``.
+
+.. _utilities-exporter:
+
+Document exporter
+=================
+
+The document exporter exports all your data from paperless into a folder for
+backup or migration to another DMS.
+
+.. code::
+
+ document_exporter target
+
+``target`` is a folder to which the data gets written. This includes documents,
+thumbnails and a ``manifest.json`` file. The manifest contains all metadata from
+the database (correspondents, tags, etc).
+
+When you use the provided docker compose script, specify ``../export`` as the
+target. This path inside the container is automatically mounted on your host on
+the folder ``export``.
+
+
+.. _utilities-importer:
+
+Document importer
+=================
+
+The document importer takes the export produced by the `Document exporter`_ and
+imports it into paperless.
+
+The importer works just like the exporter. You point it at a directory, and
+the script does the rest of the work:
+
+.. code::
+
+ document_importer source
+
+When you use the provided docker compose script, put the export inside the
+``export`` folder in your paperless source directory. Specify ``../export``
+as the ``source``.
+
+
+.. _utilities-retagger:
+
+Document retagger
+=================
+
+Say you've imported a few hundred documents and now want to introduce
+a tag or set up a new correspondent, and apply its matching to all of
+the currently-imported docs. This problem is common enough that
+there are tools for it.
+
+.. code::
+
+ document_retagger [-h] [-c] [-T] [-t] [-i] [--use-first] [-f]
+
+ optional arguments:
+ -c, --correspondent
+ -T, --tags
+ -t, --document_type
+ -i, --inbox-only
+ --use-first
+ -f, --overwrite
+
+Run this after changing or adding matching rules. It'll loop over all
+of the documents in your database and attempt to match documents
+according to the new rules.
+
+Specify any combination of ``-c``, ``-T`` and ``-t`` to have the
+retagger perform matching of the specified metadata type. If you don't
+specify any of these options, the document retagger won't do anything.
+
+Specify ``-i`` to have the document retagger work on documents tagged
+with inbox tags only. This is useful when you don't want to mess with
+your already processed documents.
+
+When multiple document types or correspondents match a single document,
+the retagger won't assign these to the document. Specify ``--use-first``
+to override this behavior and just use the first correspondent or type
+it finds. This option does not apply to tags, since any amount of tags
+can be applied to a document.
+
+Finally, ``-f`` specifies that you wish to overwrite already assigned
+correspondents, types and/or tags. The default behavior is to not
+assign correspondents and types to documents that have this data already
+assigned. ``-f`` works differently for tags: By default, only additional tags get
+added to documents, no tags will be removed. With ``-f``, tags that don't
+match a document anymore get removed as well.
+
+
+Managing the Automatic matching algorithm
+=========================================
+
+The *Auto* matching algorithm requires a trained neural network to work.
+This network needs to be updated whenever somethings in your data
+changes. The docker image takes care of that automatically with the task
+scheduler. You can manually renew the classifier by invoking the following
+management command:
+
+.. code::
+
+ document_create_classifier
+
+This command takes no arguments.
+
+.. _`administration-index`:
+
+Managing the document search index
+==================================
+
+The document search index is responsible for delivering search results for the
+website. The document index is automatically updated whenever documents get
+added to, changed, or removed from paperless. However, if the search yields
+non-existing documents or won't find anything, you may need to recreate the
+index manually.
+
+.. code::
+
+ document_index {reindex,optimize}
+
+Specify ``reindex`` to have the index created from scratch. This may take some
+time.
+
+Specify ``optimize`` to optimize the index. This updates certain aspects of
+the index and usually makes queries faster and also ensures that the
+autocompletion works properly. This command is regularly invoked by the task
+scheduler.
+
+.. _utilities-renamer:
+
+Managing filenames
+==================
+
+If you use paperless' feature to
+:ref:`assign custom filenames to your documents `,
+you can use this command to move all your files after changing
+the naming scheme.
+
+.. warning::
+
+ Since this command moves you documents around alot, it is advised to to
+ a backup before. The renaming logic is robust and will never overwrite
+ or delete a file, but you can't ever be careful enough.
+
+.. code::
+
+ document_renamer
+
+The command takes no arguments and processes all your documents at once.
+
+
+Fetching e-mail
+===============
+
+Paperless automatically fetches your e-mail every 10 minutes by default. If
+you want to invoke the email consumer manually, call the following management
+command:
+
+.. code::
+
+ mail_fetcher
+
+The command takes no arguments and processes all your mail accounts and rules.
+
+.. _utilities-archiver:
+
+Creating archived documents
+===========================
+
+Paperless stores archived PDF/A documents alongside your original documents.
+These archived documents will also contain selectable text for image-only
+originals.
+These documents are derived from the originals, which are always stored
+unmodified. If coming from an earlier version of paperless, your documents
+won't have archived versions.
+
+This command creates PDF/A documents for your documents.
+
+.. code::
+
+ document_archiver --overwrite --document
+
+This command will only attempt to create archived documents when no archived
+document exists yet, unless ``--overwrite`` is specified. If ``--document ``
+is specified, the archiver will only process that document.
+
+.. note::
+
+ This command essentially performs OCR on all your documents again,
+ according to your settings. If you run this with ``PAPERLESS_OCR_MODE=redo``,
+ it will potentially run for a very long time. You can cancel the command
+ at any time, since this command will skip already archived versions the next time
+ it is run.
+
+.. note::
+
+ Some documents will cause errors and cannot be converted into PDF/A documents,
+ such as encrypted PDF documents. The archiver will skip over these documents
+ each time it sees them.
+
+.. _utilities-encyption:
+
+Managing encryption
+===================
+
+Documents can be stored in Paperless using GnuPG encryption.
+
+.. danger::
+
+ Encryption is deprecated since paperless-ng 0.9 and doesn't really provide any
+ additional security, since you have to store the passphrase in a configuration
+ file on the same system as the encrypted documents for paperless to work.
+ Furthermore, the entire text content of the documents is stored plain in the
+ database, even if your documents are encrypted. Filenames are not encrypted as
+ well.
+
+ Also, the web server provides transparent access to your encrypted documents.
+
+ Consider running paperless on an encrypted filesystem instead, which will then
+ at least provide security against physical hardware theft.
+
+
+Enabling encryption
+-------------------
+
+Enabling encryption is no longer supported.
+
+
+Disabling encryption
+--------------------
+
+Basic usage to disable encryption of your document store:
+
+(Note: If ``PAPERLESS_PASSPHRASE`` isn't set already, you need to specify it here)
+
+.. code::
+
+ decrypt_documents [--passphrase SECR3TP4SSPHRA$E]
+
+
+.. _Pipenv: https://pipenv.pypa.io/en/latest/
\ No newline at end of file
diff --git a/docs/advanced_usage.rst b/docs/advanced_usage.rst
new file mode 100644
index 000000000..48a86384c
--- /dev/null
+++ b/docs/advanced_usage.rst
@@ -0,0 +1,342 @@
+***************
+Advanced topics
+***************
+
+Paperless offers a couple features that automate certain tasks and make your life
+easier.
+
+Guesswork
+#########
+
+
+Any document you put into the consumption directory will be consumed, but if
+you name the file right, it'll automatically set some values in the database
+for you. This is is the logic the consumer follows:
+
+1. Try to find the correspondent, title, and tags in the file name following
+ the pattern: ``Date - Correspondent - Title - tag,tag,tag.pdf``. Note that
+ the format of the date is **rigidly defined** as ``YYYYMMDDHHMMSSZ`` or
+ ``YYYYMMDDZ``. The ``Z`` refers "Zulu time" AKA "UTC".
+ The tags are optional, so the format ``Date - Correspondent - Title.pdf``
+ works as well.
+2. If that doesn't work, we skip the date and try this pattern:
+ ``Correspondent - Title - tag,tag,tag.pdf``.
+3. If that doesn't work, we try to find the correspondent and title in the file
+ name following the pattern: ``Correspondent - Title.pdf``.
+4. If that doesn't work, just assume that the name of the file is the title.
+
+So given the above, the following examples would work as you'd expect:
+
+* ``20150314000700Z - Some Company Name - Invoice 2016-01-01 - money,invoices.pdf``
+* ``20150314Z - Some Company Name - Invoice 2016-01-01 - money,invoices.pdf``
+* ``Some Company Name - Invoice 2016-01-01 - money,invoices.pdf``
+* ``Another Company - Letter of Reference.jpg``
+* ``Dad's Recipe for Pancakes.png``
+
+These however wouldn't work:
+
+* ``2015-03-14 00:07:00 UTC - Some Company Name, Invoice 2016-01-01, money, invoices.pdf``
+* ``2015-03-14 - Some Company Name, Invoice 2016-01-01, money, invoices.pdf``
+* ``Some Company Name, Invoice 2016-01-01, money, invoices.pdf``
+* ``Another Company- Letter of Reference.jpg``
+
+Do I have to be so strict about naming?
+=======================================
+
+Rather than using the strict document naming rules, one can also set the option
+``PAPERLESS_FILENAME_DATE_ORDER`` in ``paperless.conf`` to any date order
+that is accepted by dateparser_. Doing so will cause ``paperless`` to default
+to any date format that is found in the title, instead of a date pulled from
+the document's text, without requiring the strict formatting of the document
+filename as described above.
+
+.. _dateparser: https://github.com/scrapinghub/dateparser/blob/v0.7.0/docs/usage.rst#settings
+
+.. _advanced-transforming_filenames:
+
+Transforming filenames for parsing
+==================================
+
+Some devices can't produce filenames that can be parsed by the default
+parser. By configuring the option ``PAPERLESS_FILENAME_PARSE_TRANSFORMS`` in
+``paperless.conf`` one can add transformations that are applied to the filename
+before it's parsed.
+
+The option contains a list of dictionaries of regular expressions (key:
+``pattern``) and replacements (key: ``repl``) in JSON format, which are
+applied in order by passing them to ``re.subn``. Transformation stops
+after the first match, so at most one transformation is applied. The general
+syntax is
+
+.. code:: python
+
+ [{"pattern":"pattern1", "repl":"repl1"}, {"pattern":"pattern2", "repl":"repl2"}, ..., {"pattern":"patternN", "repl":"replN"}]
+
+The example below is for a Brother ADS-2400N, a scanner that allows
+different names to different hardware buttons (useful for handling
+multiple entities in one instance), but insists on adding ``_``
+to the filename.
+
+.. code:: python
+
+ # Brother profile configuration, support "Name_Date_Count" (the default
+ # setting) and "Name_Count" (use "Name" as tag and "Count" as title).
+ PAPERLESS_FILENAME_PARSE_TRANSFORMS=[{"pattern":"^([a-z]+)_(\\d{8})_(\\d{6})_([0-9]+)\\.", "repl":"\\2\\3Z - \\4 - \\1."}, {"pattern":"^([a-z]+)_([0-9]+)\\.", "repl":" - \\2 - \\1."}]
+
+
+.. _advanced-matching:
+
+Matching tags, correspondents and document types
+################################################
+
+After the consumer has tried to figure out what it could from the file name,
+it starts looking at the content of the document itself. It will compare the
+matching algorithms defined by every tag and correspondent already set in your
+database to see if they apply to the text in that document. In other words,
+if you defined a tag called ``Home Utility`` that had a ``match`` property of
+``bc hydro`` and a ``matching_algorithm`` of ``literal``, Paperless will
+automatically tag your newly-consumed document with your ``Home Utility`` tag
+so long as the text ``bc hydro`` appears in the body of the document somewhere.
+
+The matching logic is quite powerful, and supports searching the text of your
+document with different algorithms, and as such, some experimentation may be
+necessary to get things right.
+
+In order to have a tag, correspondent or type assigned automatically to newly
+consumed documents, assign a match and matching algorithm using the web
+interface. These settings define when to assign correspondents, tags and types
+to documents.
+
+The following algorithms are available:
+
+* **Any:** Looks for any occurrence of any word provided in match in the PDF.
+ If you define the match as ``Bank1 Bank2``, it will match documents containing
+ either of these terms.
+* **All:** Requires that every word provided appears in the PDF, albeit not in the
+ order provided.
+* **Literal:** Matches only if the match appears exactly as provided in the PDF.
+* **Regular expression:** Parses the match as a regular expression and tries to
+ find a match within the document.
+* **Fuzzy match:** I dont know. Look at the source.
+* **Auto:** Tries to automatically match new documents. This does not require you
+ to set a match. See the notes below.
+
+When using the "any" or "all" matching algorithms, you can search for terms
+that consist of multiple words by enclosing them in double quotes. For example,
+defining a match text of ``"Bank of America" BofA`` using the "any" algorithm,
+will match documents that contain either "Bank of America" or "BofA", but will
+not match documents containing "Bank of South America".
+
+Then just save your tag/correspondent and run another document through the
+consumer. Once complete, you should see the newly-created document,
+automatically tagged with the appropriate data.
+
+
+.. _advanced-automatic_matching:
+
+Automatic matching
+==================
+
+Paperless-ng comes with a new matching algorithm called *Auto*. This matching
+algorithm tries to assign tags, correspondents and document types to your
+documents based on how you have assigned these on existing documents. It
+uses a neural network under the hood.
+
+If, for example, all your bank statements of your account 123 at the Bank of
+America are tagged with the tag "bofa_123" and the matching algorithm of this
+tag is set to *Auto*, this neural network will examine your documents and
+automatically learn when to assign this tag.
+
+Paperless tries to hide much of the involved complexity with this approach.
+However, there are a couple caveats you need to keep in mind when using this
+feature:
+
+* Changes to your documents are not immediately reflected by the matching
+ algorithm. The neural network needs to be *trained* on your documents after
+ changes. Paperless periodically (default: once each hour) checks for changes
+ and does this automatically for you.
+* The Auto matching algorithm only takes documents into account which are NOT
+ placed in your inbox (i.e., have inbox tags assigned to them). This ensures
+ that the neural network only learns from documents which you have correctly
+ tagged before.
+* The matching algorithm can only work if there is a correlation between the
+ tag, correspondent or document type and the document itself. Your bank
+ statements usually contain your bank account number and the name of the bank,
+ so this works reasonably well, However, tags such as "TODO" cannot be
+ automatically assigned.
+* The matching algorithm needs a reasonable number of documents to identify when
+ to assign tags, correspondents, and types. If one out of a thousand documents
+ has the correspondent "Very obscure web shop I bought something five years
+ ago", it will probably not assign this correspondent automatically if you buy
+ something from them again. The more documents, the better.
+* Paperless also needs a reasonable amount of negative examples to decide when
+ not to assign a certain tag, correspondent or type. This will usually be the
+ case as you start filling up paperless with documents. Example: If all your
+ documents are either from "Webshop" and "Bank", paperless will assign one of
+ these correspondents to ANY new document, if both are set to automatic matching.
+
+Hooking into the consumption process
+####################################
+
+Sometimes you may want to do something arbitrary whenever a document is
+consumed. Rather than try to predict what you may want to do, Paperless lets
+you execute scripts of your own choosing just before or after a document is
+consumed using a couple simple hooks.
+
+Just write a script, put it somewhere that Paperless can read & execute, and
+then put the path to that script in ``paperless.conf`` with the variable name
+of either ``PAPERLESS_PRE_CONSUME_SCRIPT`` or
+``PAPERLESS_POST_CONSUME_SCRIPT``.
+
+.. important::
+
+ These scripts are executed in a **blocking** process, which means that if
+ a script takes a long time to run, it can significantly slow down your
+ document consumption flow. If you want things to run asynchronously,
+ you'll have to fork the process in your script and exit.
+
+
+Pre-consumption script
+======================
+
+Executed after the consumer sees a new document in the consumption folder, but
+before any processing of the document is performed. This script receives exactly
+one argument:
+
+* Document file name
+
+A simple but common example for this would be creating a simple script like
+this:
+
+``/usr/local/bin/ocr-pdf``
+
+.. code:: bash
+
+ #!/usr/bin/env bash
+ pdf2pdfocr.py -i ${1}
+
+``/etc/paperless.conf``
+
+.. code:: bash
+
+ ...
+ PAPERLESS_PRE_CONSUME_SCRIPT="/usr/local/bin/ocr-pdf"
+ ...
+
+This will pass the path to the document about to be consumed to ``/usr/local/bin/ocr-pdf``,
+which will in turn call `pdf2pdfocr.py`_ on your document, which will then
+overwrite the file with an OCR'd version of the file and exit. At which point,
+the consumption process will begin with the newly modified file.
+
+.. _pdf2pdfocr.py: https://github.com/LeoFCardoso/pdf2pdfocr
+
+.. _advanced-post_consume_script:
+
+Post-consumption script
+=======================
+
+Executed after the consumer has successfully processed a document and has moved it
+into paperless. It receives the following arguments:
+
+* Document id
+* Generated file name
+* Source path
+* Thumbnail path
+* Download URL
+* Thumbnail URL
+* Correspondent
+* Tags
+
+The script can be in any language you like, but for a simple shell script
+example, you can take a look at ``post-consumption-example.sh`` in the
+``scripts`` directory in this project.
+
+The post consumption script cannot cancel the consumption process.
+
+.. _advanced-file_name_handling:
+
+File name handling
+##################
+
+By default, paperless stores your documents in the media directory and renames them
+using the identifier which it has assigned to each document. You will end up getting
+files like ``0000123.pdf`` in your media directory. This isn't necessarily a bad
+thing, because you normally don't have to access these files manually. However, if
+you wish to name your files differently, you can do that by adjusting the
+``PAPERLESS_FILENAME_FORMAT`` configuration option.
+
+This variable allows you to configure the filename (folders are allowed) using
+placeholders. For example, configuring this to
+
+.. code:: bash
+
+ PAPERLESS_FILENAME_FORMAT={created_year}/{correspondent}/{title}
+
+will create a directory structure as follows:
+
+.. code::
+
+ 2019/
+ My bank/
+ Statement January.pdf
+ Statement February.pdf
+ 2020/
+ My bank/
+ Statement January.pdf
+ Letter.pdf
+ Letter_01.pdf
+ Shoe store/
+ My new shoes.pdf
+
+.. danger::
+
+ Do not manually move your files in the media folder. Paperless remembers the
+ last filename a document was stored as. If you do rename a file, paperless will
+ report your files as missing and won't be able to find them.
+
+Paperless provides the following placeholders withing filenames:
+
+* ``{correspondent}``: The name of the correspondent, or "none".
+* ``{document_type}``: The name of the document type, or "none".
+* ``{tag_list}``: A comma separated list of all tags assigned to the document.
+* ``{title}``: The title of the document.
+* ``{created}``: The full date and time the document was created.
+* ``{created_year}``: Year created only.
+* ``{created_month}``: Month created only (number 1-12).
+* ``{created_day}``: Day created only (number 1-31).
+* ``{added}``: The full date and time the document was added to paperless.
+* ``{added_year}``: Year added only.
+* ``{added_month}``: Month added only (number 1-12).
+* ``{added_day}``: Day added only (number 1-31).
+
+
+Paperless will try to conserve the information from your database as much as possible.
+However, some characters that you can use in document titles and correspondent names (such
+as ``: \ /`` and a couple more) are not allowed in filenames and will be replaced with dashes.
+
+If paperless detects that two documents share the same filename, paperless will automatically
+append ``_01``, ``_02``, etc to the filename. This happens if all the placeholders in a filename
+evaluate to the same value.
+
+.. hint::
+
+ Paperless checks the filename of a document whenever it is saved. Therefore,
+ you need to update the filenames of your documents and move them after altering
+ this setting by invoking the :ref:`document renamer `.
+
+.. warning::
+
+ Make absolutely sure you get the spelling of the placeholders right, or else
+ paperless will use the default naming scheme instead.
+
+.. caution::
+
+ As of now, you could totally tell paperless to store your files anywhere outside
+ the media directory by setting
+
+ .. code::
+
+ PAPERLESS_FILENAME_FORMAT=../../my/custom/location/{title}
+
+ However, keep in mind that inside docker, if files get stored outside of the
+ predefined volumes, they will be lost after a restart of paperless.
diff --git a/docs/api.rst b/docs/api.rst
index d08826a33..d352758fa 100644
--- a/docs/api.rst
+++ b/docs/api.rst
@@ -1,23 +1,291 @@
-.. _api:
+************
The REST API
-############
+************
-Paperless makes use of the `Django REST Framework`_ standard API interface
-because of its inherent awesomeness. Conveniently, the system is also
-self-documenting, so to learn more about the access points, schema, what's
-accepted and what isn't, you need only visit ``/api`` on your local Paperless
-installation.
+
+Paperless makes use of the `Django REST Framework`_ standard API interface.
+It provides a browsable API for most of its endpoints, which you can inspect
+at ``http://:/api/``. This also documents most of the
+available filters and ordering fields.
.. _Django REST Framework: http://django-rest-framework.org/
+The API provides 5 main endpoints:
-.. _api-uploading:
+* ``/api/documents/``: Full CRUD support, except POSTing new documents. See below.
+* ``/api/correspondents/``: Full CRUD support.
+* ``/api/document_types/``: Full CRUD support.
+* ``/api/logs/``: Read-Only.
+* ``/api/tags/``: Full CRUD support.
-Uploading
----------
+All of these endpoints except for the logging endpoint
+allow you to fetch, edit and delete individual objects
+by appending their primary key to the path, for example ``/api/documents/454/``.
-File uploads in an API are hard and so far as I've been able to tell, there's
-no standard way of accepting them, so rather than crowbar file uploads into the
-REST API and endure that headache, I've left that process to a simple HTTP
-POST, documented on the :ref:`consumption page `.
+The objects served by the document endpoint contain the following fields:
+
+* ``id``: ID of the document. Read-only.
+* ``title``: Title of the document.
+* ``content``: Plain text content of the document.
+* ``tags``: List of IDs of tags assigned to this document, or empty list.
+* ``document_type``: Document type of this document, or null.
+* ``correspondent``: Correspondent of this document or null.
+* ``created``: The date at which this document was created.
+* ``modified``: The date at which this document was last edited in paperless. Read-only.
+* ``added``: The date at which this document was added to paperless. Read-only.
+* ``archive_serial_number``: The identifier of this document in a physical document archive.
+* ``original_file_name``: Verbose filename of the original document. Read-only.
+* ``archived_file_name``: Verbose filename of the archived document. Read-only. Null if no archived document is available.
+
+
+Downloading documents
+#####################
+
+In addition to that, the document endpoint offers these additional actions on
+individual documents:
+
+* ``/api/documents//download/``: Download the document.
+* ``/api/documents//preview/``: Display the document inline,
+ without downloading it.
+* ``/api/documents//thumb/``: Download the PNG thumbnail of a document.
+
+Paperless generates archived PDF/A documents from consumed files and stores both
+the original files as well as the archived files. By default, the endpoints
+for previews and downloads serve the archived file, if it is available.
+Otherwise, the original file is served.
+Some document cannot be archived.
+
+The endpoints correctly serve the response header fields ``Content-Disposition``
+and ``Content-Type`` to indicate the filename for download and the type of content of
+the document.
+
+In order to download or preview the original document when an archied document is available,
+supply the query parameter ``original=true``.
+
+.. hint::
+
+ Paperless used to provide these functionality at ``/fetch//preview``,
+ ``/fetch//thumb`` and ``/fetch//doc``. Redirects to the new URLs
+ are in place. However, if you use these old URLs to access documents, you
+ should update your app or script to use the new URLs.
+
+
+Getting document metadata
+#########################
+
+The api also has an endpoint to retrieve read-only metadata about specific documents. this
+information is not served along with the document objects, since it requires reading
+files and would therefore slow down document lists considerably.
+
+Access the metadata of a document with an ID ``id`` at ``/api/documents//metadata/``.
+
+The endpoint reports the following data:
+
+* ``original_checksum``: MD5 checksum of the original document.
+* ``original_size``: Size of the original document, in bytes.
+* ``original_mime_type``: Mime type of the original document.
+* ``media_filename``: Current filename of the document, under which it is stored inside the media directory.
+* ``has_archive_version``: True, if this document is archived, false otherwise.
+* ``original_metadata``: A list of metadata associated with the original document. See below.
+* ``archive_checksum``: MD5 checksum of the archived document, or null.
+* ``archive_size``: Size of the archived document in bytes, or null.
+* ``archive_metadata``: Metadata associated with the archived document, or null. See below.
+
+File metadata is reported as a list of objects in the following form:
+
+.. code:: json
+
+ [
+ {
+ "namespace": "http://ns.adobe.com/pdf/1.3/",
+ "prefix": "pdf",
+ "key": "Producer",
+ "value": "SparklePDF, Fancy edition"
+ },
+ ]
+
+``namespace`` and ``prefix`` can be null. The actual metadata reported depends on the file type and the metadata
+available in that specific document. Paperless only reports PDF metadata at this point.
+
+Authorization
+#############
+
+The REST api provides three different forms of authentication.
+
+1. Basic authentication
+
+ Authorize by providing a HTTP header in the form
+
+ .. code::
+
+ Authorization: Basic
+
+ where ``credentials`` is a base64-encoded string of ``:``
+
+2. Session authentication
+
+ When you're logged into paperless in your browser, you're automatically
+ logged into the API as well and don't need to provide any authorization
+ headers.
+
+3. Token authentication
+
+ Paperless also offers an endpoint to acquire authentication tokens.
+
+ POST a username and password as a form or json string to ``/api/token/``
+ and paperless will respond with a token, if the login data is correct.
+ This token can be used to authenticate other requests with the
+ following HTTP header:
+
+ .. code::
+
+ Authorization: Token
+
+ Tokens can be managed and revoked in the paperless admin.
+
+Searching for documents
+#######################
+
+Paperless-ng offers API endpoints for full text search. These are as follows:
+
+``/api/search/``
+================
+
+Get search results based on a query.
+
+Query parameters:
+
+* ``query``: The query string. See
+ `here `_
+ for details on the syntax.
+* ``page``: Specify the page you want to retrieve. Each page
+ contains 10 search results and the first page is ``page=1``, which
+ is the default if this is omitted.
+
+Result list object returned by the endpoint:
+
+.. code:: json
+
+ {
+ "count": 1,
+ "page": 1,
+ "page_count": 1,
+ "corrected_query": "",
+ "results": [
+
+ ]
+ }
+
+* ``count``: The approximate total number of results.
+* ``page``: The page returned to you. This might be different from
+ the page you requested, if you requested a page that is behind
+ the last page. In that case, the last page is returned.
+* ``page_count``: The total number of pages.
+* ``corrected_query``: Corrected version of the query string. Can be null.
+ If not null, can be used verbatim to start a new query.
+* ``results``: A list of result objects on the current page.
+
+Result object:
+
+.. code:: json
+
+ {
+ "id": 1,
+ "highlights": [
+
+ ],
+ "score": 6.34234,
+ "rank": 23,
+ "document": {
+
+ }
+ }
+
+* ``id``: the primary key of the found document
+* ``highlights``: an object containing parsable highlights for the result.
+ See below.
+* ``score``: The score assigned to the document. A higher score indicates a
+ better match with the query. Search results are sorted descending by score.
+* ``rank``: the position of the document within the entire search results list.
+* ``document``: The full json of the document, as returned by
+ ``/api/documents//``.
+
+Highlights object:
+
+Highlights are provided as a list of fragments. A fragment is a longer section of
+text from the original document.
+Each fragment contains a list of strings, and some of them are marked as a highlight.
+
+.. code:: json
+
+ [
+ [
+ {"text": "This is a sample text with a "},
+ {"text": "highlighted", "term": 0},
+ {"text": " word."}
+ ],
+ [
+ {"text": "Another", "term": 1},
+ {"text": " fragment with a highlight."}
+ ]
+ ]
+
+
+
+When ``term`` is present within a string, the word within ``text`` should be highlighted.
+The term index groups multiple matches together and words with the same index
+should get identical highlighting.
+A client may use this example to produce the following output:
+
+... This is a sample text with a **highlighted** word. ... **Another** fragment with a highlight. ...
+
+``/api/search/autocomplete/``
+=============================
+
+Get auto completions for a partial search term.
+
+Query parameters:
+
+* ``term``: The incomplete term.
+* ``limit``: Amount of results. Defaults to 10.
+
+Results returned by the endpoint are ordered by importance of the term in the
+document index. The first result is the term that has the highest Tf/Idf score
+in the index.
+
+.. code:: json
+
+ [
+ "term1",
+ "term3",
+ "term6",
+ "term4"
+ ]
+
+
+.. _api-file_uploads:
+
+POSTing documents
+#################
+
+The API provides a special endpoint for file uploads:
+
+``/api/documents/post_document/``
+
+POST a multipart form to this endpoint, where the form field ``document`` contains
+the document that you want to upload to paperless. The filename is sanitized and
+then used to store the document in a temporary directory, and the consumer will
+be instructed to consume the document from there.
+
+The endpoint supports the following optional form fields:
+
+* ``title``: Specify a title that the consumer should use for the document.
+* ``correspondent``: Specify the ID of a correspondent that the consumer should use for the document.
+* ``document_type``: Similar to correspondent.
+* ``tags``: Similar to correspondent. Specify this multiple times to have multiple tags added
+ to the document.
+
+The endpoint will immediately return "OK" if the document consumption process
+was started successfully. No additional status information about the consumption
+process itself is available, since that happens in a different process.
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 8a0528a88..a993eb530 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,4 +1,333 @@
+
+.. _paperless_changelog:
+
+*********
Changelog
+*********
+
+
+paperless-ng 0.9.8
+##################
+
+This release addresses two severe issues with the previous release.
+
+* The delete buttons for document types, correspondents and tags were not working.
+* The document section in the admin was causing internal server errors (500).
+
+
+paperless-ng 0.9.7
+##################
+
+
+* Front end
+
+ * Thanks to the hard work of `Michael Shamoon`_, paperless now comes with a much more streamlined UI for
+ filtering documents.
+
+ * `Michael Shamoon`_ replaced the document preview with another component. This should fix compatibility with Safari browsers.
+
+ * Added buttons to the management pages to quickly show all documents with one specific tag, correspondent, or title.
+
+ * Paperless now stores your saved views on the server and associates them with your user account.
+ This means that you can access your views on multiple devices and have separate views for different users.
+ You will have to recreate your views.
+
+ * The GitHub and documentation links now open in new tabs/windows. Thanks to `rYR79435`_.
+
+ * Paperless now generates default saved view names when saving views with certain filter rules.
+
+ * Added a small version indicator to the front end.
+
+* Other additions and changes
+
+ * The new filename format field ``{tag_list}`` inserts a list of tags into the filename, separated by comma.
+ * The ``document_retagger`` no longer removes inbox tags or tags without matching rules.
+ * The new configuration option ``PAPERLESS_COOKIE_PREFIX`` allows you to run multiple instances of paperless on different ports.
+ This option enables you to be logged in into multiple instances by specifying different cookie names for each instance.
+
+* Fixes
+
+ * Sometimes paperless would assign dates in the future to newly consumed documents.
+ * The filename format fields ``{created_month}`` and ``{created_day}`` now use a leading zero for single digit values.
+ * The filename format field ``{tags}`` can no longer be used without arguments.
+ * Paperless was not able to consume many images (especially images from mobile scanners) due to missing DPI information.
+ Paperless now assumes A4 paper size for PDF generation if no DPI information is present.
+ * Documents with empty titles could not be opened from the table view due to the link being empty.
+ * Fixed an issue with filenames containing special characters such as ``:`` not being accepted for upload.
+ * Fixed issues with thumbnail generation for plain text files.
+
+
+paperless-ng 0.9.6
+##################
+
+This release focusses primarily on many small issues with the UI.
+
+* Front end
+
+ * Paperless now has proper window titles.
+ * Fixed an issue with the small cards when more than 7 tags were used.
+ * Navigation of the "Show all" links adjusted. They navigate to the saved view now, if available in the sidebar.
+ * Some indication on the document lists that a filter is active was added.
+ * There's a new filter to filter for documents that do *not* have a certain tag.
+ * The file upload box now shows upload progress.
+ * The document edit page was reorganized.
+ * The document edit page shows various information about a document.
+ * An issue with the height of the preview was fixed.
+ * Table issues with too long document titles fixed.
+
+* API
+
+ * The API now serves file names with documents.
+ * The API now serves various metadata about documents.
+ * API documentation updated.
+
+* Other
+
+ * Fixed an issue with the docker image when a non-standard PostgreSQL port was used.
+ * The docker image was trying check for installed languages before actually installing them.
+ * ``FILENAME_FORMAT`` placeholder for document types.
+ * The filename formatter is now less restrictive with file names and tries to
+ conserve the original correspondents, types and titles as much as possible.
+ * The filename formatter does not include the document ID in filenames anymore. It will
+ rather append ``_01``, ``_02``, etc when it detects duplicate filenames.
+
+.. note::
+
+ The changes to the filename format will apply to newly added documents and changed documents.
+ If you want all files to reflect these changes, execute the ``document_renamer`` management
+ command.
+
+
+paperless-ng 0.9.5
+##################
+
+This release concludes the big changes I wanted to get rolled into paperless. The next releases before 1.0 will
+focus on fixing issues, primarily.
+
+* OCR
+
+ * Paperless now uses `OCRmyPDF `_ to perform OCR on documents.
+ It still uses tesseract under the hood, but the PDF parser of Paperless has changed considerably and
+ will behave different for some douments.
+ * OCRmyPDF creates archived PDF/A documents with embedded text that can be selected in the front end.
+ * Paperless stores archived versions of documents alongside with the originals. The originals can be
+ accessed on the document edit page. If available, a dropdown menu will appear next to the download button.
+ * Many of the configuration options regarding OCR have changed. See :ref:`configuration-ocr` for details.
+ * Paperless no longer guesses the language of your documents. It always uses the language that you
+ specified with ``PAPERLESS_OCR_LANGUAGE``. Be sure to set this to the language the majority of your
+ documents are in. Multiple languages can be specified, but that requires more CPU time.
+ * The management command :ref:`document_archiver ` can be used to create archived versions for already
+ existing documents.
+
+* Tags from consumption folder.
+
+ * Thanks to `jayme-github`_, paperless now consumes files from sub folders in the consumption folder and is able to assign tags
+ based on the sub folders a document was found in. This can be configured with ``PAPERLESS_CONSUMER_RECURSIVE`` and
+ ``PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS``.
+
+* API
+
+ * The API now offers token authentication.
+ * The endpoint for uploading documents now supports specifying custom titles, correspondents, tags and types.
+ This can be used by clients to override the default behavior of paperless. See :ref:`api-file_uploads`.
+ * The document endpoint of API now serves documents in this form:
+
+ * correspondents, document types and tags are referenced by their ID in the fields ``correspondent``, ``document_type`` and ``tags``. The ``*_id`` versions are gone. These fields are read/write.
+ * paperless does not serve nested tags, correspondents or types anymore.
+
+* Front end
+
+ * Paperless does some basic caching of correspondents, tags and types and will only request them from the server when necessary or when entirely reloading the page.
+ * Document list fetching is about 10%-30% faster now, especially when lots of tags/correspondents are present.
+ * Some minor improvements to the front end, such as document count in the document list, better highlighting of the current page, and improvements to the filter behavior.
+
+* Fixes:
+
+ * A bug with the generation of filenames for files with unsupported types caused the exporter and
+ document saving to crash.
+ * Mail handling no longer exits entirely when encountering errors. It will skip the account/rule/message on which the error occured.
+ * Assigning correspondents from mail sender names failed for very long names. Paperless no longer assigns correspondents in these cases.
+
+paperless-ng 0.9.4
+##################
+
+* Searching:
+
+ * Paperless now supports searching by tags, types and dates and correspondents. In order to have this applied to your
+ existing documents, you need to perform a ``document_index reindex`` management command
+ (see :ref:`administration-index`)
+ that adds the data to the search index. You only need to do this once, since the schema of the search index changed.
+ Paperless keeps the index updated after that whenever something changes.
+ * Paperless now has spelling corrections ("Did you mean") for miss-typed queries.
+ * The documentation contains :ref:`information about the query syntax `.
+
+* Front end:
+
+ * Clickable tags, correspondents and types allow quick filtering for related documents.
+ * Saved views are now editable.
+ * Preview documents directly in the browser.
+ * Navigation from the dashboard to saved views.
+
+* Fixes:
+
+ * A severe error when trying to use post consume scripts.
+ * An error in the consumer that cause invalid messages of missing files to show up in the log.
+
+* The documentation now contains information about bare metal installs and a section about
+ how to setup the development environment.
+
+paperless-ng 0.9.3
+##################
+
+* Setting ``PAPERLESS_AUTO_LOGIN_USERNAME`` replaces ``PAPERLESS_DISABLE_LOGIN``.
+ You have to specify your username.
+* Added a simple sanity checker that checks your documents for missing or orphaned files,
+ files with wrong checksums, inaccessible files, and documents with empty content.
+* It is no longer possible to encrypt your documents. For the time being, paperless will
+ continue to operate with already encrypted documents.
+* Fixes:
+
+ * Paperless now uses inotify again, since the watchdog was causing issues which I was not
+ aware of.
+ * Issue with the automatic classifier not working with only one tag.
+ * A couple issues with the search index being opened to eagerly.
+
+* Added lots of tests for various parts of the application.
+
+paperless-ng 0.9.2
+##################
+
+* Major changes to the front end (colors, logo, shadows, layout of the cards,
+ better mobile support)
+
+* Paperless now uses mime types and libmagic detection to determine
+ if a file type is supported and which parser to use. Removes all
+ file type checks that where present in MANY different places in
+ paperless.
+
+* Mail consumer now correctly consumes documents even when their
+ content type was not set correctly. (i.e. PDF documents with
+ content type ``application/octet-stream``)
+
+* Basic sorting of mail rules added
+
+* Much better admin for mail rule editing.
+
+* Docker entrypoint script awaits the database server if it is
+ configured.
+
+* Disabled editing of logs.
+
+* New setting ``PAPERLESS_OCR_PAGES`` limits the tesseract parser
+ to the first n pages of scanned documents.
+
+* Fixed a bug where tasks with too long task names would not show
+ up in the admin.
+
+paperless-ng 0.9.1
+##################
+
+* Moved documentation of the settings to the actual documentation.
+* Updated release script to force the user to choose between SQLite
+ and PostgreSQL. This avoids confusion when upgrading from paperless.
+
+
+paperless-ng 0.9.0
+##################
+
+* **Deprecated:** GnuPG. :ref:`See this note on the state of GnuPG in paperless-ng. `
+ This features will most likely be removed in future versions.
+
+* **Added:** New frontend. Features:
+
+ * Single page application: It's much more responsive than the django admin pages.
+ * Dashboard. Shows recently scanned documents, or todo notes, or other documents
+ at wish. Allows uploading of documents. Shows basic statistics.
+ * Better document list with multiple display options.
+ * Full text search with result highlighting, auto completion and scoring based
+ on the query. It uses a document search index in the background.
+ * Saveable filters.
+ * Better log viewer.
+
+* **Added:** Document types. Assign these to documents just as correspondents.
+ They may be used in the future to perform automatic operations on documents
+ depending on the type.
+* **Added:** Inbox tags. Define an inbox tag and it will automatically be
+ assigned to any new document scanned into the system.
+* **Added:** Automatic matching. A new matching algorithm that automatically
+ assigns tags, document types and correspondents to your documents. It uses
+ a neural network trained on your data.
+* **Added:** Archive serial numbers. Assign these to quickly find documents stored in
+ physical binders.
+* **Added:** Enabled the internal user management of django. This isn't really a
+ multi user solution, however, it allows more than one user to access the website
+ and set some basic permissions / renew passwords.
+
+* **Modified [breaking]:** All new mail consumer with customizable filters, actions and
+ multiple account support. Replaces the old mail consumer. The new mail consumer
+ needs different configuration but can be configured to act exactly like the old
+ consumer.
+
+
+* **Modified:** Changes to the consumer:
+
+ * Now uses the excellent watchdog library that should make sure files are
+ discovered no matter what the platform is.
+ * The consumer now uses a task scheduler to run consumption processes in parallel.
+ This means that consuming many documents should be much faster on systems with
+ many cores.
+ * Concurrency is controlled with the new settings ``PAPERLESS_TASK_WORKERS``
+ and ``PAPERLESS_THREADS_PER_WORKER``. See TODO for details on concurrency.
+ * The consumer no longer blocks the database for extended periods of time.
+ * An issue with tesseract running multiple threads per page and slowing down
+ the consumer was fixed.
+
+* **Modified [breaking]:** REST Api changes:
+
+ * New filters added, other filters removed (case sensitive filters, slug filters)
+ * Endpoints for thumbnails, previews and downloads replace the old ``/fetch/`` urls. Redirects are in place.
+ * Endpoint for document uploads replaces the old ``/push`` url. Redirects are in place.
+ * Foreign key relationships are now served as IDs, not as urls.
+
+* **Modified [breaking]:** PostgreSQL:
+
+ * If ``PAPERLESS_DBHOST`` is specified in the settings, paperless uses PostgreSQL instead of SQLite.
+ Username, database and password all default to ``paperless`` if not specified.
+
+* **Modified [breaking]:** document_retagger management command rework. See
+ :ref:`utilities-retagger` for details. Replaces ``document_correspondents``
+ management command.
+* **Removed [breaking]:** Reminders.
+* **Removed:** All customizations made to the django admin pages.
+* **Removed [breaking]:** The docker image no longer supports SSL. If you want to expose
+ paperless to the internet, hide paperless behind a proxy server that handles SSL
+ requests.
+* **Internal changes:** Mostly code cleanup, including:
+
+ * Rework of the code of the tesseract parser. This is now a lot cleaner.
+ * Rework of the filename handling code. It was a mess.
+ * Fixed some issues with the document exporter not exporting all documents when encountering duplicate filenames.
+ * Added a task scheduler that takes care of checking mail, training the classifier, maintaining the document search index
+ and consuming documents.
+ * Updated dependencies. Now uses Pipenv all around.
+ * Updated Dockerfile and docker-compose. Now uses ``supervisord`` to run everything paperless-related in a single container.
+
+* **Settings:**
+
+ * ``PAPERLESS_FORGIVING_OCR`` is now default and gone. Reason: Even if ``langdetect`` fails to detect
+ a language, tesseract still does a very good job at ocr'ing a document with the default language.
+ Certain language specifics such as umlauts may not get picked up properly.
+ * ``PAPERLESS_DEBUG`` defaults to ``false``.
+ * The presence of ``PAPERLESS_DBHOST`` now determines whether to use PostgreSQL or
+ SQLite.
+ * ``PAPERLESS_OCR_THREADS`` is gone and replaced with ``PAPERLESS_TASK_WORKERS`` and
+ ``PAPERLESS_THREADS_PER_WORKER``. Refer to the config example for details.
+ * ``PAPERLESS_OPTIMIZE_THUMBNAILS`` allows you to disable or enable thumbnail
+ optimization. This is useful on less powerful devices.
+
+* Many more small changes here and there. The usual stuff.
+
+Paperless
#########
2.7.0
@@ -6,7 +335,7 @@ Changelog
* `syntonym`_ submitted a pull request to catch IMAP connection errors `#475`_.
* `Stéphane Brunner`_ added ``psycopg2`` to the Pipfile `#489`_. He also fixed
- a syntax error in ``docker-compose.yml.example`` `#488`_ and added [DjangoQL](https://github.com/ivelum/djangoql),
+ a syntax error in ``docker-compose.yml.example`` `#488`_ and added `DjangoQL`_,
which allows a litany of handy search functionality `#492`_.
* `CkuT`_ and `JOKer`_ hacked out a simple, but super-helpful optimisation to
how the thumbnails are served up, improving performance considerably `#481`_.
@@ -194,7 +523,7 @@ that it was more an annoyance than anything else, so this feature is now turned
off unless you explicitly set a passphrase in your config file.
Migrating from 1.x
-------------------
+==================
Encryption isn't gone, it's just off for new users. So long as you have
``PAPERLESS_PASSPHRASE`` set in your config or your environment, Paperless
@@ -564,6 +893,9 @@ bulk of the work on this big change.
* Initial release
+.. _rYR79435: https://github.com/rYR79435
+.. _Michael Shamoon: https://github.com/shamoon
+.. _jayme-github: http://github.com/jayme-github
.. _Brian Conn: https://github.com/TheConnMan
.. _Christopher Luu: https://github.com/nuudles
.. _Florian Jung: https://github.com/the01
@@ -739,6 +1071,6 @@ bulk of the work on this big change.
.. _#489: https://github.com/the-paperless-project/paperless/pull/489
.. _#492: https://github.com/the-paperless-project/paperless/pull/492
-.. _pipenv: https://docs.pipenv.org/
.. _a new home on Docker Hub: https://hub.docker.com/r/danielquinn/paperless/
.. _optipng: http://optipng.sourceforge.net/
+.. _DjangoQL: https://github.com/ivelum/djangoql
diff --git a/docs/conf.py b/docs/conf.py
index 7cf8c9fe1..b2442ddc9 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,51 +1,21 @@
-# -*- coding: utf-8 -*-
-#
-# Paperless documentation build configuration file, created by
-# sphinx-quickstart on Mon Oct 26 18:36:52 2015.
-#
-# This file is execfile()d with the current directory set to its
-# containing dir.
-#
-# Note that not all possible configuration values are present in this
-# autogenerated file.
-#
-# All configuration values have a default; values that are commented out
-# serve to show the default.
+import sphinx_rtd_theme
-import sys
-import os
__version__ = None
exec(open("../src/paperless/version.py").read())
-# Believe it or not, this is the officially sanctioned way to add custom CSS.
-def setup(app):
- app.add_stylesheet("custom.css")
-
-# If extensions (or modules to document with autodoc) are in another directory,
-# add these directories to sys.path here. If the directory is relative to the
-# documentation root, use os.path.abspath to make it absolute, like shown here.
-#sys.path.insert(0, os.path.abspath('.'))
-
-# -- General configuration ------------------------------------------------
-
-# If your documentation needs a minimal Sphinx version, state it here.
-#needs_sphinx = '1.0'
-
-# Add any Sphinx extension module names here, as strings. They can be
-# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
-# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.intersphinx',
'sphinx.ext.todo',
'sphinx.ext.imgmath',
'sphinx.ext.viewcode',
+ 'sphinx_rtd_theme',
]
# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+# templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
@@ -57,7 +27,7 @@ source_suffix = '.rst'
master_doc = 'index'
# General information about the project.
-project = u'Paperless'
+project = u'Paperless-ng'
copyright = u'2015, Daniel Quinn'
# The version info for the project you're documenting, acts as replacement for
@@ -118,7 +88,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
-html_theme = 'default'
+html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@@ -198,19 +168,6 @@ html_static_path = ['_static']
# Output file base name for HTML help builder.
htmlhelp_basename = 'paperless'
-
-#
-# Attempt to use the ReadTheDocs theme. If it's not installed, fallback to
-# the default.
-#
-
-try:
- import sphinx_rtd_theme
- html_theme = "sphinx_rtd_theme"
- html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
-except ImportError:
- pass
-
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
diff --git a/docs/configuration.rst b/docs/configuration.rst
new file mode 100644
index 000000000..d3f47215b
--- /dev/null
+++ b/docs/configuration.rst
@@ -0,0 +1,426 @@
+.. _configuration:
+
+*************
+Configuration
+*************
+
+Paperless provides a wide range of customizations.
+Depending on how you run paperless, these settings have to be defined in different
+places.
+
+* If you run paperless on docker, ``paperless.conf`` is not used. Rather, configure
+ paperless by copying necessary options to ``docker-compose.env``.
+* If you are running paperless on anything else, paperless will search for the
+ configuration file in these locations and use the first one it finds:
+
+ .. code::
+
+ /path/to/paperless/paperless.conf
+ /etc/paperless.conf
+ /usr/local/etc/paperless.conf
+
+
+Required services
+#################
+
+PAPERLESS_REDIS=
+ This is required for processing scheduled tasks such as email fetching, index
+ optimization and for training the automatic document matcher.
+
+ Defaults to redis://localhost:6379.
+
+PAPERLESS_DBHOST=
+ By default, sqlite is used as the database backend. This can be changed here.
+ Set PAPERLESS_DBHOST and PostgreSQL will be used instead of mysql.
+
+PAPERLESS_DBPORT=
+ Adjust port if necessary.
+
+ Default is 5432.
+
+PAPERLESS_DBNAME=
+ Database name in PostgreSQL.
+
+ Defaults to "paperless".
+
+PAPERLESS_DBUSER=
+ Database user in PostgreSQL.
+
+ Defaults to "paperless".
+
+PAPERLESS_DBPASS=
+ Database password for PostgreSQL.
+
+ Defaults to "paperless".
+
+
+Paths and folders
+#################
+
+PAPERLESS_CONSUMPTION_DIR=
+ This where your documents should go to be consumed. Make sure that it exists
+ and that the user running the paperless service can read/write its contents
+ before you start Paperless.
+
+ Don't change this when using docker, as it only changes the path within the
+ container. Change the local consumption directory in the docker-compose.yml
+ file instead.
+
+ Defaults to "../consume", relative to the "src" directory.
+
+PAPERLESS_DATA_DIR=
+ This is where paperless stores all its data (search index, SQLite database,
+ classification model, etc).
+
+ Defaults to "../data", relative to the "src" directory.
+
+PAPERLESS_MEDIA_ROOT=
+ This is where your documents and thumbnails are stored.
+
+ You can set this and PAPERLESS_DATA_DIR to the same folder to have paperless
+ store all its data within the same volume.
+
+ Defaults to "../media", relative to the "src" directory.
+
+PAPERLESS_STATICDIR=
+ Override the default STATIC_ROOT here. This is where all static files
+ created using "collectstatic" manager command are stored.
+
+ Unless you're doing something fancy, there is no need to override this.
+
+ Defaults to "../static", relative to the "src" directory.
+
+PAPERLESS_FILENAME_FORMAT=
+ Changes the filenames paperless uses to store documents in the media directory.
+ See :ref:`advanced-file_name_handling` for details.
+
+ Default is none, which disables this feature.
+
+Hosting & Security
+##################
+
+PAPERLESS_SECRET_KEY=
+ Paperless uses this to make session tokens. If you expose paperless on the
+ internet, you need to change this, since the default secret is well known.
+
+ Use any sequence of characters. The more, the better. You don't need to
+ remember this. Just face-roll your keyboard.
+
+ Default is listed in the file ``src/paperless/settings.py``.
+
+PAPERLESS_ALLOWED_HOSTS
+ If you're planning on putting Paperless on the open internet, then you
+ really should set this value to the domain name you're using. Failing to do
+ so leaves you open to HTTP host header attacks:
+ https://docs.djangoproject.com/en/3.1/topics/security/#host-header-validation
+
+ Just remember that this is a comma-separated list, so "example.com" is fine,
+ as is "example.com,www.example.com", but NOT " example.com" or "example.com,"
+
+ Defaults to "*", which is all hosts.
+
+PAPERLESS_CORS_ALLOWED_HOSTS
+ You need to add your servers to the list of allowed hosts that can do CORS
+ calls. Set this to your public domain name.
+
+ Defaults to "http://localhost:8000".
+
+PAPERLESS_FORCE_SCRIPT_NAME=
+ To host paperless under a subpath url like example.com/paperless you set
+ this value to /paperless. No trailing slash!
+
+ .. note::
+
+ I don't know if this works in paperless-ng. Probably not.
+
+ Defaults to none, which hosts paperless at "/".
+
+PAPERLESS_STATIC_URL=
+ Override the STATIC_URL here. Unless you're hosting Paperless off a
+ subdomain like /paperless/, you probably don't need to change this.
+
+ Defaults to "/static/".
+
+PAPERLESS_AUTO_LOGIN_USERNAME=
+ Specify a username here so that paperless will automatically perform login
+ with the selected user.
+
+ .. danger::
+
+ Do not use this when exposing paperless on the internet. There are no
+ checks in place that would prevent you from doing this.
+
+ Defaults to none, which disables this feature.
+
+
+PAPERLESS_COOKIE_PREFIX=
+ Specify a prefix that is added to the cookies used by paperless to identify
+ the currently logged in user. This is useful for when you're running two
+ instances of paperless on the same host.
+
+ After changing this, you will have to login again.
+
+ Defaults to ``""``, which does not alter the cookie names.
+
+.. _configuration-ocr:
+
+OCR settings
+############
+
+Paperless uses `OCRmyPDF `_ for
+performing OCR on documents and images. Paperless uses sensible defaults for
+most settings, but all of them can be configured to your needs.
+
+
+PAPERLESS_OCR_LANGUAGE=
+ Customize the language that paperless will attempt to use when
+ parsing documents.
+
+ It should be a 3-letter language code consistent with ISO
+ 639: https://www.loc.gov/standards/iso639-2/php/code_list.php
+
+ Set this to the language most of your documents are written in.
+
+ This can be a combination of multiple languages such as ``deu+eng``,
+ in which case tesseract will use whatever language matches best.
+ Keep in mind that tesseract uses much more cpu time with multiple
+ languages enabled.
+
+ Defaults to "eng".
+
+PAPERLESS_OCR_MODE=
+ Tell paperless when and how to perform ocr on your documents. Four modes
+ are available:
+
+ * ``skip``: Paperless skips all pages and will perform ocr only on pages
+ where no text is present. This is the safest option.
+ * ``skip_noarchive``: In addition to skip, paperless won't create an
+ archived version of your documents when it finds any text in them.
+ This is useful if you don't want to have two almost-identical versions
+ of your digital documents in the media folder. This is the fastest option.
+ * ``redo``: Paperless will OCR all pages of your documents and attempt to
+ replace any existing text layers with new text. This will be useful for
+ documents from scanners that already performed OCR with insufficient
+ results. It will also perform OCR on purely digital documents.
+
+ This option may fail on some documents that have features that cannot
+ be removed, such as forms. In this case, the text from the document is
+ used instead.
+ * ``force``: Paperless rasterizes your documents, converting any text
+ into images and puts the OCRed text on top. This works for all documents,
+ however, the resulting document may be significantly larger and text
+ won't appear as sharp when zoomed in.
+
+ The default is ``skip``, which only performs OCR when necessary and always
+ creates archived documents.
+
+PAPERLESS_OCR_OUTPUT_TYPE=
+ Specify the the type of PDF documents that paperless should produce.
+
+ * ``pdf``: Modify the PDF document as little as possible.
+ * ``pdfa``: Convert PDF documents into PDF/A-2b documents, which is a
+ subset of the entire PDF specification and meant for storing
+ documents long term.
+ * ``pdfa-1``, ``pdfa-2``, ``pdfa-3`` to specify the exact version of
+ PDF/A you wish to use.
+
+ If not specified, ``pdfa`` is used. Remember that paperless also keeps
+ the original input file as well as the archived version.
+
+
+PAPERLESS_OCR_PAGES=
+ Tells paperless to use only the specified amount of pages for OCR. Documents
+ with less than the specified amount of pages get OCR'ed completely.
+
+ Specifying 1 here will only use the first page.
+
+ When combined with ``PAPERLESS_OCR_MODE=redo`` or ``PAPERLESS_OCR_MODE=force``,
+ paperless will not modify any text it finds on excluded pages and copy it
+ verbatim.
+
+ Defaults to 0, which disables this feature and always uses all pages.
+
+
+PAPERLESS_OCR_IMAGE_DPI=
+ Paperless will OCR any images you put into the system and convert them
+ into PDF documents. This is useful if your scanner produces images.
+ In order to do so, paperless needs to know the DPI of the image.
+ Most images from scanners will have this information embedded and
+ paperless will detect and use that information. In case this fails, it
+ uses this value as a fallback.
+
+ Set this to the DPI your scanner produces images at.
+
+ Default is none, which causes paperless to fail if no DPI information is
+ present in an image.
+
+
+PAPERLESS_OCR_USER_ARG=
+ OCRmyPDF offers many more options. Use this parameter to specify any
+ additional arguments you wish to pass to OCRmyPDF. Since Paperless uses
+ the API of OCRmyPDF, you have to specify these in a format that can be
+ passed to the API. See `the API reference of OCRmyPDF `_
+ for valid parameters. All command line options are supported, but they
+ use underscores instead of dashed.
+
+ .. caution::
+
+ Paperless has been tested to work with the OCR options provided
+ above. There are many options that are incompatible with each other,
+ so specifying invalid options may prevent paperless from consuming
+ any documents.
+
+ Specify arguments as a JSON dictionary. Keep note of lower case booleans
+ and double quoted parameter names and strings. Examples:
+
+ .. code:: json
+
+ {"deskew": true, "optimize": 3, "unpaper_args": "--pre-rotate 90"}
+
+
+Software tweaks
+###############
+
+PAPERLESS_TASK_WORKERS=
+ Paperless does multiple things in the background: Maintain the search index,
+ maintain the automatic matching algorithm, check emails, consume documents,
+ etc. This variable specifies how many things it will do in parallel.
+
+
+PAPERLESS_THREADS_PER_WORKER=
+ Furthermore, paperless uses multiple threads when consuming documents to
+ speed up OCR. This variable specifies how many pages paperless will process
+ in parallel on a single document.
+
+ .. caution::
+
+ Ensure that the product
+
+ PAPERLESS_TASK_WORKERS * PAPERLESS_THREADS_PER_WORKER
+
+ does not exceed your CPU core count or else paperless will be extremely slow.
+ If you want paperless to process many documents in parallel, choose a high
+ worker count. If you want paperless to process very large documents faster,
+ use a higher thread per worker count.
+
+ The default is a balance between the two, according to your CPU core count,
+ with a slight favor towards threads per worker, and using as much cores as
+ possible.
+
+ If you only specify PAPERLESS_TASK_WORKERS, paperless will adjust
+ PAPERLESS_THREADS_PER_WORKER automatically.
+
+
+PAPERLESS_TIME_ZONE=
+ Set the time zone here.
+ See https://docs.djangoproject.com/en/3.1/ref/settings/#std:setting-TIME_ZONE
+ for details on how to set it.
+
+ Defaults to UTC.
+
+
+PAPERLESS_CONSUMER_POLLING=
+ If paperless won't find documents added to your consume folder, it might
+ not be able to automatically detect filesystem changes. In that case,
+ specify a polling interval in seconds here, which will then cause paperless
+ to periodically check your consumption directory for changes.
+
+ Defaults to 0, which disables polling and uses filesystem notifications.
+
+
+PAPERLESS_CONSUMER_DELETE_DUPLICATES=
+ When the consumer detects a duplicate document, it will not touch the
+ original document. This default behavior can be changed here.
+
+ Defaults to false.
+
+
+PAPERLESS_CONSUMER_RECURSIVE=
+ Enable recursive watching of the consumption directory. Paperless will
+ then pickup files from files in subdirectories within your consumption
+ directory as well.
+
+ Defaults to false.
+
+
+PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=
+ Set the names of subdirectories as tags for consumed files.
+ E.g. /foo/bar/file.pdf will add the tags "foo" and "bar" to
+ the consumed file. Paperless will create any tags that don't exist yet.
+
+ PAPERLESS_CONSUMER_RECURSIVE must be enabled for this to work.
+
+ Defaults to false.
+
+
+PAPERLESS_CONVERT_MEMORY_LIMIT=
+ On smaller systems, or even in the case of Very Large Documents, the consumer
+ may explode, complaining about how it's "unable to extend pixel cache". In
+ such cases, try setting this to a reasonably low value, like 32. The
+ default is to use whatever is necessary to do everything without writing to
+ disk, and units are in megabytes.
+
+ For more information on how to use this value, you should search
+ the web for "MAGICK_MEMORY_LIMIT".
+
+ Defaults to 0, which disables the limit.
+
+PAPERLESS_CONVERT_TMPDIR=
+ Similar to the memory limit, if you've got a small system and your OS mounts
+ /tmp as tmpfs, you should set this to a path that's on a physical disk, like
+ /home/your_user/tmp or something. ImageMagick will use this as scratch space
+ when crunching through very large documents.
+
+ For more information on how to use this value, you should search
+ the web for "MAGICK_TMPDIR".
+
+ Default is none, which disables the temporary directory.
+
+PAPERLESS_OPTIMIZE_THUMBNAILS=
+ Use optipng to optimize thumbnails. This usually reduces the size of
+ thumbnails by about 20%, but uses considerable compute time during
+ consumption.
+
+ Defaults to true.
+
+PAPERLESS_POST_CONSUME_SCRIPT=
+ After a document is consumed, Paperless can trigger an arbitrary script if
+ you like. This script will be passed a number of arguments for you to work
+ with. For more information, take a look at :ref:`advanced-post_consume_script`.
+
+ The default is blank, which means nothing will be executed.
+
+PAPERLESS_FILENAME_DATE_ORDER=
+ Paperless will check the document text for document date information.
+ Use this setting to enable checking the document filename for date
+ information. The date order can be set to any option as specified in
+ https://dateparser.readthedocs.io/en/latest/settings.html#date-order.
+ The filename will be checked first, and if nothing is found, the document
+ text will be checked as normal.
+
+ Defaults to none, which disables this feature.
+
+PAPERLESS_FILENAME_PARSE_TRANSFORMS
+ Transforms filenames before they are processed by paperless. See
+ :ref:`advanced-transforming_filenames` for details.
+
+ Defaults to none, which disables this feature.
+
+Binaries
+########
+
+There are a few external software packages that Paperless expects to find on
+your system when it starts up. Unless you've done something creative with
+their installation, you probably won't need to edit any of these. However,
+if you've installed these programs somewhere where simply typing the name of
+the program doesn't automatically execute it (ie. the program isn't in your
+$PATH), then you'll need to specify the literal path for that program.
+
+PAPERLESS_CONVERT_BINARY=
+ Defaults to "/usr/bin/convert".
+
+PAPERLESS_GS_BINARY=
+ Defaults to "/usr/bin/gs".
+
+PAPERLESS_OPTIPNG_BINARY=
+ Defaults to "/usr/bin/optipng".
diff --git a/docs/consumption.rst b/docs/consumption.rst
deleted file mode 100644
index 15f6c6393..000000000
--- a/docs/consumption.rst
+++ /dev/null
@@ -1,255 +0,0 @@
-.. _consumption:
-
-Consumption
-###########
-
-Once you've got Paperless setup, you need to start feeding documents into it.
-Currently, there are three options: the consumption directory, IMAP (email), and
-HTTP POST.
-
-
-.. _consumption-directory:
-
-The Consumption Directory
-=========================
-
-The primary method of getting documents into your database is by putting them in
-the consumption directory. The ``document_consumer`` script runs in an infinite
-loop looking for new additions to this directory and when it finds them, it goes
-about the process of parsing them with the OCR, indexing what it finds, and
-encrypting the PDF (if ``PAPERLESS_PASSPHRASE`` is set), storing it in the
-media directory.
-
-Getting stuff into this directory is up to you. If you're running Paperless
-on your local computer, you might just want to drag and drop files there, but if
-you're running this on a server and want your scanner to automatically push
-files to this directory, you'll need to setup some sort of service to accept the
-files from the scanner. Typically, you're looking at an FTP server like
-`Proftpd`_ or `Samba`_.
-
-.. _Proftpd: http://www.proftpd.org/
-.. _Samba: http://www.samba.org/
-
-So where is this consumption directory? It's wherever you define it. Look for
-the ``CONSUMPTION_DIR`` value in ``settings.py``. Set that to somewhere
-appropriate for your use and put some documents in there. When you're ready,
-follow the :ref:`consumer ` instructions to get it running.
-
-
-.. _consumption-directory-hook:
-
-Hooking into the Consumption Process
-------------------------------------
-
-Sometimes you may want to do something arbitrary whenever a document is
-consumed. Rather than try to predict what you may want to do, Paperless lets
-you execute scripts of your own choosing just before or after a document is
-consumed using a couple simple hooks.
-
-Just write a script, put it somewhere that Paperless can read & execute, and
-then put the path to that script in ``paperless.conf`` with the variable name
-of either ``PAPERLESS_PRE_CONSUME_SCRIPT`` or
-``PAPERLESS_POST_CONSUME_SCRIPT``. The script will be executed before or
-or after the document is consumed respectively.
-
-.. important::
-
- These scripts are executed in a **blocking** process, which means that if
- a script takes a long time to run, it can significantly slow down your
- document consumption flow. If you want things to run asynchronously,
- you'll have to fork the process in your script and exit.
-
-
-.. _consumption-directory-hook-variables:
-
-What Can These Scripts Do?
-..........................
-
-It's your script, so you're only limited by your imagination and the laws of
-physics. However, the following values are passed to the scripts in order:
-
-
-.. _consumption-director-hook-variables-pre:
-
-Pre-consumption script
-::::::::::::::::::::::
-
-* Document file name
-
-A simple but common example for this would be creating a simple script like
-this:
-
-``/usr/local/bin/ocr-pdf``
-
-.. code:: bash
-
- #!/usr/bin/env bash
- pdf2pdfocr.py -i ${1}
-
-``/etc/paperless.conf``
-
-.. code:: bash
-
- ...
- PAPERLESS_PRE_CONSUME_SCRIPT="/usr/local/bin/ocr-pdf"
- ...
-
-This will pass the path to the document about to be consumed to ``/usr/local/bin/ocr-pdf``,
-which will in turn call `pdf2pdfocr.py`_ on your document, which will then
-overwrite the file with an OCR'd version of the file and exit. At which point,
-the consumption process will begin with the newly modified file.
-
-.. _pdf2pdfocr.py: https://github.com/LeoFCardoso/pdf2pdfocr
-
-
-.. _consumption-director-hook-variables-post:
-
-Post-consumption script
-:::::::::::::::::::::::
-
-* Document id
-* Generated file name
-* Source path
-* Thumbnail path
-* Download URL
-* Thumbnail URL
-* Correspondent
-* Tags
-
-The script can be in any language you like, but for a simple shell script
-example, you can take a look at ``post-consumption-example.sh`` in the
-``scripts`` directory in this project.
-
-
-.. _consumption-imap:
-
-IMAP (Email)
-============
-
-Another handy way to get documents into your database is to email them to
-yourself. The typical use-case would be to be out for lunch and want to send a
-copy of the receipt back to your system at home. Paperless can be taught to
-pull emails down from an arbitrary account and dump them into the consumption
-directory where the process :ref:`above ` will follow the
-usual pattern on consuming the document.
-
-Some things you need to know about this feature:
-
-* It's disabled by default. By setting the values below it will be enabled.
-* It's been tested in a limited environment, so it may not work for you (please
- submit a pull request if you can!)
-* It's designed to **delete mail from the server once consumed**. So don't go
- pointing this to your personal email account and wonder where all your stuff
- went.
-* Currently, only one photo (attachment) per email will work.
-
-So, with all that in mind, here's what you do to get it running:
-
-1. Setup a new email account somewhere, or if you're feeling daring, create a
- folder in an existing email box and note the path to that folder.
-2. In ``/etc/paperless.conf`` set all of the appropriate values in
- ``PATHS AND FOLDERS`` and ``SECURITY``.
- If you decided to use a subfolder of an existing account, then make sure you
- set ``PAPERLESS_CONSUME_MAIL_INBOX`` accordingly here. You also have to set
- the ``PAPERLESS_EMAIL_SECRET`` to something you can remember 'cause you'll
- have to include that in every email you send.
-3. Restart the :ref:`consumer `. The consumer will check
- the configured email account at startup and from then on every 10 minutes
- for something new and pulls down whatever it finds.
-4. Send yourself an email! Note that the subject is treated as the file name,
- so if you set the subject to ``Correspondent - Title - tag,tag,tag``, you'll
- get what you expect. Also, you must include the aforementioned secret
- string in every email so the fetcher knows that it's safe to import.
- Note that Paperless only allows the email title to consist of safe characters
- to be imported. These consist of alpha-numeric characters and ``-_ ,.'``.
-5. After a few minutes, the consumer will poll your mailbox, pull down the
- message, and place the attachment in the consumption directory with the
- appropriate name. A few minutes later, the consumer will import it like any
- other file.
-
-
-.. _consumption-http:
-
-HTTP POST
-=========
-
-You can also submit a document via HTTP POST, so long as you do so after
-authenticating. To push your document to Paperless, send an HTTP POST to the
-server with the following name/value pairs:
-
-* ``correspondent``: The name of the document's correspondent. Note that there
- are restrictions on what characters you can use here. Specifically,
- alphanumeric characters, `-`, `,`, `.`, and `'` are ok, everything else is
- out. You also can't use the sequence ` - ` (space, dash, space).
-* ``title``: The title of the document. The rules for characters is the same
- here as the correspondent.
-* ``document``: The file you're uploading
-
-Specify ``enctype="multipart/form-data"``, and then POST your file with::
-
- Content-Disposition: form-data; name="document"; filename="whatever.pdf"
-
-An example of this in HTML is a typical form:
-
-.. code:: html
-
-
-
-But a potentially more useful way to do this would be in Python. Here we use
-the requests library to handle basic authentication and to send the POST data
-to the URL.
-
-.. code:: python
-
- import os
-
- from hashlib import sha256
-
- import requests
- from requests.auth import HTTPBasicAuth
-
- # You authenticate via BasicAuth or with a session id.
- # We use BasicAuth here
- username = "my-username"
- password = "my-super-secret-password"
-
- # Where you have Paperless installed and listening
- url = "http://localhost:8000/push"
-
- # Document metadata
- correspondent = "Test Correspondent"
- title = "Test Title"
-
- # The local file you want to push
- path = "/path/to/some/directory/my-document.pdf"
-
-
- with open(path, "rb") as f:
-
- response = requests.post(
- url=url,
- data={"title": title, "correspondent": correspondent},
- files={"document": (os.path.basename(path), f, "application/pdf")},
- auth=HTTPBasicAuth(username, password),
- allow_redirects=False
- )
-
- if response.status_code == 202:
-
- # Everything worked out ok
- print("Upload successful")
-
- else:
-
- # If you don't get a 202, it's probably because your credentials
- # are wrong or something. This will give you a rough idea of what
- # happened.
-
- print("We got HTTP status code: {}".format(response.status_code))
- for k, v in response.headers.items():
- print("{}: {}".format(k, v))
diff --git a/docs/contributing.rst b/docs/contributing.rst
index 9b18861a8..30eb9779a 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -3,6 +3,10 @@
Contributing to Paperless
#########################
+.. warning::
+
+ This section is not updated to paperless-ng yet.
+
Maybe you've been using Paperless for a while and want to add a feature or two,
or maybe you've come across a bug that you have some ideas how to solve. The
beauty of Free software is that you can see what's wrong and help to get it
@@ -81,7 +85,7 @@ quoted, or triple-quoted string will do:
problematic_string = 'This is a "string" with "quotes" in it'
In HTML templates, please use double-quotes for tag attributes, and single
-quotes for arguments passed to Django tempalte tags:
+quotes for arguments passed to Django template tags:
.. code:: html
diff --git a/docs/customising.rst b/docs/customising.rst
deleted file mode 100644
index 0d8e428cd..000000000
--- a/docs/customising.rst
+++ /dev/null
@@ -1,42 +0,0 @@
-.. _customising:
-
-Customising Paperless
-#####################
-
-Currently, the Paperless' interface is just the default Django admin, which
-while powerful, is rather boring. If you'd like to give the site a bit of a
-face-lift, or if you simply want to adjust the colours, contrast, or font size
-to make things easier to read, you can do that by adding your own CSS or
-Javascript quite easily.
-
-
-.. _customising-overrides:
-
-Overrides
-=========
-
-On every page load, Paperless looks for two files in your media root directory
-(the directory defined by your ``PAPERLESS_MEDIADIR`` configuration variable or
-the default, ``/media/``) for two files:
-
-* ``overrides.css``
-* ``overrides.js``
-
-If it finds either or both of those files, they'll be loaded into the page: the
-CSS in the ````, and the Javascript stuffed into the last line of the
-````.
-
-
-.. _customising-overrides-note:
-
-An important note about customisation
--------------------------------------
-
-Any changes you make to the site with your CSS or Javascript are likely to
-depend on the structure of the current HTML and/or the existing CSS rules. For
-the most part it's safe to assume that these bits won't change, but *sometimes
-they do* as features are added or bugs are fixed.
-
-If you make a change that you think others would appreciate though, submit it
-as a pull request and maybe we can find a way to work it into the project by
-default!
\ No newline at end of file
diff --git a/docs/examples/lxc/lxc-install.sh b/docs/examples/lxc/lxc-install.sh
deleted file mode 100644
index b53858e3a..000000000
--- a/docs/examples/lxc/lxc-install.sh
+++ /dev/null
@@ -1,158 +0,0 @@
-#!/usr/bin/env bash
-
-# Bash script to install paperless in lxc containter
-# paperless.lan
-#
-# Will set-up paperless, apache2 and proftpd
-#
-# lxc launch ubuntu: paperless
-# lxc exec paperless -- sh -c "sudo apt-get update && sudo apt-get install -y wget"
-# lxc exec paperless -- sh -c "wget https://raw.githubusercontent.com/the-paperless-project/paperless/master/docs/examples/lxc/lxc-install.sh && /bin/bash lxc-install.sh --email "
-#
-#
-set +e
-PASSWORD=$(< /dev/urandom tr -dc _A-Z-a-z-0-9+@%^{} | head -c20;echo;)
-EMAIL=
-
-function displayHelp() {
- echo "available parameters:
- -e | --email
- -p | --password
- "
-}
-
-POSITIONAL=()
-while [[ $# -gt 0 ]]
-do
-key="$1"
-i=$key
-
-case $i in
- -e|--email)
- EMAIL="${2}"
- shift
- shift
- ;;
- -p|--password)
- PASSWORD="${2}"
- shift
- shift
- ;;
- --default|-h|--help)
- shift
- displayHelp
- exit 0
- ;;
- *)
- echo "argument: $i not recognized"
- exit 2
- ;;
-esac
-done
-set -- "${POSITIONAL[@]}" # restore positional parameters
-
-if [ -z $EMAIL ]; then
- echo "missing email, try running with -h "
- exit 3
-fi
-if [[ $(/usr/bin/id -u) -ne 0 ]]; then
- echo "Not running as root"
- exit
-fi
-
-if [ $(grep -c paperless /etc/passwd) -eq 0 ]; then
- # Add paperless user with no password
- adduser --disabled-password --gecos "" paperless
-fi
-
-if [ $(grep -c ftpupload /etc/passwd) -eq 0 ]; then
- # Add ftpupload
- adduser --disabled-password --gecos "" ftpupload
- echo "Set ftpupload password: "
- #passwd ftpupload
- #TODO: generate some password and allow parameter
- echo "ftpupload:ftpuploadpassword" | chpasswd
-fi
-
-if [ $(id -nG paperless | grep -Fcw ftpupload) -eq 0 ]; then
- # Allow paperless group to access
- adduser paperless ftpupload
- chmod g+w /home/ftpupload
-fi
-
-# Get apt up to date
-apt-get update
-
-# Needed for plain Paperless
-apt-get -y install unpaper gnupg libpoppler-cpp-dev python3-pyocr tesseract-ocr imagemagick optipng git
-
-# Needed for Apache
-apt-get -y install apache2 libapache2-mod-wsgi-py3
-
-if [ ! -f /etc/proftpd/proftpd.conf ]; then
- # Install ftp server and make sure all uplaoded files are owned by paperless
- apt-get -y install proftpd
-fi
-if [ $(grep -c paperless /etc/proftpd/proftpd.conf) -eq 0 ]; then
- cat <> /etc/proftpd/proftpd.conf
-
- UserOwner paperless
- GroupOwner paperless
-
-EOF
- systemctl restart proftpd
-fi
-
-#Get Paperless from git
-su -c "cd /home/paperless ; git clone https://github.com/the-paperless-project/paperless" paperless
-
-# Install Pip Requirements
-apt-get -y install python3-pip python3-venv
-cd /home/paperless/paperless
-pip3 install -r requirements.txt
-
-# Take paperless.conf.example and set consumuption dir (ftp dir)
-sed -e '/PAPERLESS_CONSUMPTION_DIR=/s/=.*/=\"\/home\/ftpupload\/\"/' \
- /home/paperless/paperless/paperless.conf.example >/etc/paperless.conf
-
-# Update /etc/paperless.conf with PAPERLESS_SECRET_KEY
-SECRET=$(strings /dev/urandom | grep -o '[[:alnum:]]' | head -n 30 | tr -d '\n'; echo)
-sed -i "s/#PAPERLESS_SECRET_KEY.*/PAPERLESS_SECRET_KEY=$SECRET/" /etc/paperless.conf
-
-#Initialise the SQLite database
-su -c "cd /home/paperless/paperless/src/ ; ./manage.py migrate" paperless
-echo "if superuser doesn't exists, create one with login: paperless and password: ${PASSWORD}"
-#Create a user for your Paperless instance
-su -c "cd /home/paperless/paperless/src/ ; echo ./manage.py create_superuser_with_password --username paperless --email ${EMAIL} --password ${PASSWORD} --preserve" paperless
-su -c "cd /home/paperless/paperless/src/ ; ./manage.py create_superuser_with_password --username paperless --email ${EMAIL} --password ${PASSWORD} --preserve" paperless
-
-if [ ! -d /home/paperless/paperless/static ]; then
- # 167 static files copied to '/home/paperless/paperless/static'.
- su -c "cd /home/paperless/paperless/src/ ; ./manage.py collectstatic" paperless
-fi
-
-if [ ! -f /etc/apache2/sites-available/paperless.conf ]; then
- # Set-up apache
- cp /home/paperless/paperless/docs/examples/lxc/paperless.conf /etc/apache2/sites-available/
- a2dissite 000-default.conf
- a2ensite paperless.conf
- systemctl reload apache2
-fi
-
-sed -e "s:home/paperless/project/virtualenv/bin/python:usr/bin/python3:" \
- /home/paperless/paperless/scripts/paperless-consumer.service \
- >/etc/systemd/system/paperless-consumer.service
-
-sed -i "s:/home/paperless/project/src/manage.py:/home/paperless/paperless/src/manage.py:" \
- /etc/systemd/system/paperless-consumer.service
-
-
-systemctl enable paperless-consumer
-systemctl start paperless-consumer
-
-# convert-im6.q16: not authorized
-# Security risk ?
-# https://stackoverflow.com/questions/42928765/convertnot-authorized-aaaa-error-constitute-c-readimage-453
-if [ -f /etc/ImageMagick-6/policy.xml ]; then
- mv /etc/ImageMagick-6/policy.xml /etc/ImageMagick-6/policy.xmlout
-fi
diff --git a/docs/examples/lxc/paperless.conf b/docs/examples/lxc/paperless.conf
deleted file mode 100644
index 7a6978a65..000000000
--- a/docs/examples/lxc/paperless.conf
+++ /dev/null
@@ -1,18 +0,0 @@
-
- ServerName paperless.lan
-
- Alias /static/ /home/paperless/paperless/static/
-
- Require all granted
-
-
- WSGIScriptAlias / /home/paperless/paperless/src/paperless/wsgi.py
- WSGIDaemonProcess paperless.lan user=paperless group=paperless threads=5 python-path=/home/paperless/paperless/src
- WSGIProcessGroup paperless.lan
-
-
-
- Require all granted
-
-
-
diff --git a/docs/extending.rst b/docs/extending.rst
index 2afc6367b..28da1f56b 100644
--- a/docs/extending.rst
+++ b/docs/extending.rst
@@ -1,112 +1,197 @@
.. _extending:
+Paperless development
+#####################
+
+This section describes the steps you need to take to start development on paperless-ng.
+
+1. Check out the source from github. The repository is organized in the following way:
+
+ * ``master`` always represents the latest release and will only see changes
+ when a new release is made.
+ * ``dev`` contains the code that will be in the next release.
+ * ``feature-X`` contain bigger changes that will be in some release, but not
+ necessarily the next one.
+
+ Apart from that, the folder structure is as follows:
+
+ * ``docs/`` - Documentation.
+ * ``src-ui/`` - Code of the front end.
+ * ``src/`` - Code of the back end.
+ * ``scripts/`` - Various scripts that help with different parts of development.
+ * ``docker/`` - Files required to build the docker image.
+
+2. Install some dependencies.
+
+ * Python 3.6.
+ * All dependencies listed in the :ref:`Bare metal route `
+ * redis. You can either install redis or use the included scritps/start-redis.sh
+ to use docker to fire up a redis instance.
+
+Back end development
+====================
+
+The backend is a django application. I use PyCharm for development, but you can use whatever
+you want.
+
+Install the python dependencies by performing ``pipenv install --dev`` in the src/ directory.
+This will also create a virtual environment, which you can enter with ``pipenv shell`` or
+execute one-shot commands in with ``pipenv run``.
+
+In ``src/paperless.conf``, enable debug mode.
+
+Configure the IDE to use the src/ folder as the base source folder. Configure the following
+launch configurations in your IDE:
+
+* python3 manage.py runserver
+* python3 manage.py qcluster
+* python3 manage.py consumer
+
+Depending on which part of paperless you're developing for, you need to have some or all of
+them running.
+
+Testing and code style:
+
+* Run ``pytest`` in the src/ directory to execute all tests. This also generates a HTML coverage
+ report. When runnings test, paperless.conf is loaded as well. However: the tests rely on the default
+ configuration. This is not ideal. But for now, make sure no settings except for DEBUG are overridden when testing.
+* Run ``pycodestyle`` to test your code for issues with the configured code style settings.
+
+ .. note::
+
+ The line length rule E501 is generally useful for getting multiple source files
+ next to each other on the screen. However, in some cases, its just not possible
+ to make some lines fit, especially complicated IF cases. Append `` # NOQA: E501``
+ to disable this check for certain lines.
+
+Front end development
+=====================
+
+The front end is build using angular. I use the ``Code - OSS`` IDE for development.
+
+In order to get started, you need ``npm``. Install the Angular CLI interface with
+
+.. code:: shell-session
+
+ $ npm install -g @angular/cli
+
+and make sure that it's on your path. Next, in the src-ui/ directory, install the
+required dependencies of the project.
+
+.. code:: shell-session
+
+ $ npm install
+
+You can launch a development server by running
+
+.. code:: shell-session
+
+ $ ng serve
+
+This will automatically update whenever you save. However, in-place compilation might fail
+on syntax errors, in which case you need to restart it.
+
+By default, the development server is available on ``http://localhost:4200/`` and is configured
+to access the API at ``http://localhost:8000/api/``, which is the default of the backend.
+If you enabled DEBUG on the back end, several security overrides for allowed hosts, CORS and
+X-Frame-Options are in place so that the front end behaves exactly as in production. This also
+relies on you being logged into the back end. Without a valid session, The front end will simply
+not work.
+
+In order to build the front end and serve it as part of django, execute
+
+.. code:: shell-session
+
+ $ ng build --prod --output-path ../src/documents/static/frontend/
+
+This will build the front end and put it in a location from which the Django server will serve
+it as static content. This way, you can verify that authentication is working.
+
+Making a release
+================
+
+Execute the ``make-release.sh `` script.
+
+This will test and assemble everything and also build and tag a docker image.
+
+
Extending Paperless
===================
-For the most part, Paperless is monolithic, so extending it is often best
-managed by way of modifying the code directly and issuing a pull request on
-`GitHub`_. However, over time the project has been evolving to be a little
-more "pluggable" so that users can write their own stuff that talks to it.
+Paperless does not have any fancy plugin systems and will probably never have. However,
+some parts of the application have been designed to allow easy integration of additional
+features without any modification to the base code.
-.. _GitHub: https://github.com/the-paperless-project/paperless
+Making custom parsers
+---------------------
+Paperless uses parsers to add documents to paperless. A parser is responsible for:
-.. _extending-parsers:
+* Retrieve the content from the original
+* Create a thumbnail
+* Optional: Retrieve a created date from the original
+* Optional: Create an archived document from the original
-Parsers
--------
+Custom parsers can be added to paperless to support more file types. In order to do that,
+you need to write the parser itself and announce its existence to paperless.
-You can leverage Paperless' consumption model to have it consume files *other*
-than ones handled by default like ``.pdf``, ``.jpg``, and ``.tiff``. To do so,
-you simply follow Django's convention of creating a new app, with a few key
-requirements.
-
-
-.. _extending-parsers-parserspy:
-
-parsers.py
-..........
-
-In this file, you create a class that extends
-``documents.parsers.DocumentParser`` and go about implementing the three
-required methods:
-
-* ``get_thumbnail()``: Returns the path to a file we can use as a thumbnail for
- this document.
-* ``get_text()``: Returns the text from the document and only the text.
-* ``get_date()``: If possible, this returns the date of the document, otherwise
- it should return ``None``.
-
-
-.. _extending-parsers-signalspy:
-
-signals.py
-..........
-
-At consumption time, Paperless emits a ``document_consumer_declaration``
-signal which your module has to react to in order to let the consumer know
-whether or not it's capable of handling a particular file. Think of it like
-this:
-
-1. Consumer finds a file in the consumption directory.
-2. It asks all the available parsers: *"Hey, can you handle this file?"*
-3. Each parser responds with either ``None`` meaning they can't handle the
- file, or a dictionary in the following format:
+The parser itself must extend ``documents.parsers.DocumentParser`` and must implement the
+methods ``parse`` and ``get_thumbnail``. You can provide your own implementation to
+``get_date`` if you don't want to rely on paperless' default date guessing mechanisms.
.. code:: python
- {
- "parser": ,
- "weight":
- }
+ class MyCustomParser(DocumentParser):
-The consumer compares the ``weight`` values from all respondents and uses the
-class with the highest value to consume the document. The default parser,
-``RasterisedDocumentParser`` has a weight of ``0``.
+ def parse(self, document_path, mime_type):
+ # This method does not return anything. Rather, you should assign
+ # whatever you got from the document to the following fields:
+ # The content of the document.
+ self.text = "content"
+
+ # Optional: path to a PDF document that you created from the original.
+ self.archive_path = os.path.join(self.tempdir, "archived.pdf")
-.. _extending-parsers-appspy:
+ # Optional: "created" date of the document.
+ self.date = get_created_from_metadata(document_path)
-apps.py
-.......
+ def get_thumbnail(self, document_path, mime_type):
+ # This should return the path to a thumbnail you created for this
+ # document.
+ return os.path.join(self.tempdir, "thumb.png")
-This is a standard Django file, but you'll need to add some code to it to
-connect your parser to the ``document_consumer_declaration`` signal.
+If you encounter any issues during parsing, raise a ``documents.parsers.ParseError``.
+The ``self.tempdir`` directory is a temporary directory that is guaranteed to be empty
+and removed after consumption finished. You can use that directory to store any
+intermediate files and also use it to store the thumbnail / archived document.
-.. _extending-parsers-finally:
-
-Finally
-.......
-
-The last step is to update ``settings.py`` to include your new module.
-Eventually, this will be dynamic, but at the moment, you have to edit the
-``INSTALLED_APPS`` section manually. Simply add the path to your AppConfig to
-the list like this:
+After that, you need to announce your parser to paperless. You need to connect a
+handler to the ``document_consumer_declaration`` signal. Have a look in the file
+``src/paperless_tesseract/apps.py`` on how that's done. The handler is a method
+that returns information about your parser:
.. code:: python
- INSTALLED_APPS = [
- ...
- "my_module.apps.MyModuleConfig",
- ...
- ]
+ def myparser_consumer_declaration(sender, **kwargs):
+ return {
+ "parser": MyCustomParser,
+ "weight": 0,
+ "mime_types": {
+ "application/pdf": ".pdf",
+ "image/jpeg": ".jpg",
+ }
+ }
-Order doesn't matter, but generally it's a good idea to place your module lower
-in the list so that you don't end up accidentally overriding project defaults
-somewhere.
+* ``parser`` is a reference to a class that extends ``DocumentParser``.
+* ``weight`` is used whenever two or more parsers are able to parse a file: The parser with
+ the higher weight wins. This can be used to override the parsers provided by
+ paperless.
-.. _extending-parsers-example:
-
-An Example
-..........
-
-The core Paperless functionality is based on this design, so if you want to see
-what a parser module should look like, have a look at `parsers.py`_,
-`signals.py`_, and `apps.py`_ in the `paperless_tesseract`_ module.
-
-.. _parsers.py: https://github.com/the-paperless-project/paperless/blob/master/src/paperless_tesseract/parsers.py
-.. _signals.py: https://github.com/the-paperless-project/paperless/blob/master/src/paperless_tesseract/signals.py
-.. _apps.py: https://github.com/the-paperless-project/paperless/blob/master/src/paperless_tesseract/apps.py
-.. _paperless_tesseract: https://github.com/the-paperless-project/paperless/blob/master/src/paperless_tesseract/
+* ``mime_types`` is a dictionary. The keys are the mime types your parser supports and the value
+ is the default file extension that paperless should use when storing files and serving them for
+ download. We could guess that from the file extensions, but some mime types have many extensions
+ associated with them and the python methods responsible for guessing the extension do not always
+ return the same value.
diff --git a/docs/faq.rst b/docs/faq.rst
new file mode 100644
index 000000000..d9efddd0f
--- /dev/null
+++ b/docs/faq.rst
@@ -0,0 +1,106 @@
+
+**************************
+Frequently asked questions
+**************************
+
+**Q:** *What's the general plan for Paperless-ng?*
+
+**A:** Paperless-ng is already almost feature-complete. This project will remain
+as simple as it is right now. It will see improvements to features that are already there.
+If you need advanced features such as document versions,
+workflows or multi-user with customizable access to individual files, this is
+not the tool for you.
+
+Features that *are* planned are some more quality of life extensions for the searching
+(i.e., search for similar documents, group results by correspondents with "more from this"
+links, etc), bulk editing and hierarchical tags.
+
+**Q:** *I'm using docker. Where are my documents?*
+
+**A:** Your documents are stored inside the docker volume ``paperless_media``.
+Docker manages this volume automatically for you. It is a persistent storage
+and will persist as long as you don't explicitly delete it. The actual location
+depends on your host operating system. On Linux, chances are high that this location
+is
+
+.. code::
+
+ /var/lib/docker/volumes/paperless_media/_data
+
+.. caution::
+
+ Do not mess with this folder. Don't change permissions and don't move
+ files around manually. This folder is meant to be entirely managed by docker
+ and paperless.
+
+**Q:** *Let's say you don't support this project anymore in a year. Can I easily move to other systems?*
+
+**A:** Your documents are stored as plain files inside the media folder. You can always drag those files
+out of that folder to use them elsewhere. Here are a couple notes about that.
+
+* Paperless never modifies your original documents. It keeps checksums of all documents and uses a
+ scheduled sanity checker to check that they remain the same.
+* By default, paperless uses the internal ID of each document as its filename. This might not be very
+ convenient for export. However, you can adjust the way files are stored in paperless by
+ :ref:`configuring the filename format `.
+* :ref:`The exporter ` is another easy way to get your files out of paperless with reasonable file names.
+
+**Q:** *What file types does paperless-ng support?*
+
+**A:** Currently, the following files are supported:
+
+* PDF documents, PNG images, JPEG images, TIFF images and GIF images are processed with OCR and converted into PDF documents.
+* Plain text documents are supported as well and are added verbatim
+ to paperless.
+
+Paperless determines the type of a file by inspecting its content. The
+file extensions do not matter.
+
+**Q:** *Will paperless-ng run on Raspberry Pi?*
+
+**A:** The short answer is yes. I've tested it on a Raspberry Pi 3 B.
+The long answer is that certain parts of
+Paperless will run very slow, such as the tesseract OCR. On Raspberry Pi,
+try to OCR documents before feeding them into paperless so that paperless can
+reuse the text. The web interface should be a lot snappier, since it runs
+in your browser and paperless has to do much less work to serve the data.
+
+.. note::
+
+ You can adjust some of the settings so that paperless uses less processing
+ power. See :ref:`setup-less_powerful_devices` for details.
+
+
+**Q:** *How do I install paperless-ng on Raspberry Pi?*
+
+**A:** There is no docker image for ARM available. If you know how to build
+that automatically, I'm all ears. For now, you have to grab the latest release
+archive from the project page and build the image yourself. The release comes
+with the front end already compiled, so you don't have to do this on the Pi.
+
+**Q:** *How do I run this on unRaid?*
+
+**A:** Head over to ``_,
+`Uli Fahrer `_ created a container template for that.
+I don't exactly know how to use that though, since I don't use unRaid.
+
+**Q:** *How do I run this on my toaster?*
+
+**A:** I honestly don't know! As for all other devices that might be able
+to run paperless, you're a bit on your own. If you can't run the docker image,
+the documentation has instructions for bare metal installs. I'm running
+paperless on an i3 processor from 2015 or so. This is also what I use to test
+new releases with. Apart from that, I also have a Raspberry Pi, which I
+occasionally build the image on and see if it works.
+
+**Q:** *How do I proxy this with NGINX?*
+
+.. code::
+
+ location / {
+ proxy_pass http://localhost:8000/
+ }
+
+And that's about it. Paperless serves everything, including static files by itself
+when running the docker image. If you want to do anything fancy, you have to
+install paperless bare metal.
diff --git a/docs/guesswork.rst b/docs/guesswork.rst
deleted file mode 100644
index c12ecd0c4..000000000
--- a/docs/guesswork.rst
+++ /dev/null
@@ -1,131 +0,0 @@
-.. _guesswork:
-
-Guesswork
-#########
-
-During the consumption process, Paperless tries to guess some of the attributes
-of the document it's looking at. To do this it uses two approaches:
-
-
-.. _guesswork-naming:
-
-File Naming
-===========
-
-Any document you put into the consumption directory will be consumed, but if
-you name the file right, it'll automatically set some values in the database
-for you. This is is the logic the consumer follows:
-
-1. Try to find the correspondent, title, and tags in the file name following
- the pattern: ``Date - Correspondent - Title - tag,tag,tag.pdf``. Note that
- the format of the date is **rigidly defined** as ``YYYYMMDDHHMMSSZ`` or
- ``YYYYMMDDZ``. The ``Z`` refers "Zulu time" AKA "UTC".
- The tags are optional, so the format ``Date - Correspondent - Title.pdf``
- works as well.
-2. If that doesn't work, we skip the date and try this pattern:
- ``Correspondent - Title - tag,tag,tag.pdf``.
-3. If that doesn't work, we try to find the correspondent and title in the file
- name following the pattern: ``Correspondent - Title.pdf``.
-4. If that doesn't work, just assume that the name of the file is the title.
-
-So given the above, the following examples would work as you'd expect:
-
-* ``20150314000700Z - Some Company Name - Invoice 2016-01-01 - money,invoices.pdf``
-* ``20150314Z - Some Company Name - Invoice 2016-01-01 - money,invoices.pdf``
-* ``Some Company Name - Invoice 2016-01-01 - money,invoices.pdf``
-* ``Another Company - Letter of Reference.jpg``
-* ``Dad's Recipe for Pancakes.png``
-
-These however wouldn't work:
-
-* ``2015-03-14 00:07:00 UTC - Some Company Name, Invoice 2016-01-01, money, invoices.pdf``
-* ``2015-03-14 - Some Company Name, Invoice 2016-01-01, money, invoices.pdf``
-* ``Some Company Name, Invoice 2016-01-01, money, invoices.pdf``
-* ``Another Company- Letter of Reference.jpg``
-
-Do I have to be so strict about naming?
----------------------------------------
-Rather than using the strict document naming rules, one can also set the option
-``PAPERLESS_FILENAME_DATE_ORDER`` in ``paperless.conf`` to any date order
-that is accepted by dateparser_. Doing so will cause ``paperless`` to default
-to any date format that is found in the title, instead of a date pulled from
-the document's text, without requiring the strict formatting of the document
-filename as described above.
-
-.. _dateparser: https://github.com/scrapinghub/dateparser/blob/v0.7.0/docs/usage.rst#settings
-
-Transforming filenames for parsing
-----------------------------------
-Some devices can't produce filenames that can be parsed by the default
-parser. By configuring the option ``PAPERLESS_FILENAME_PARSE_TRANSFORMS`` in
-``paperless.conf`` one can add transformations that are applied to the filename
-before it's parsed.
-
-The option contains a list of dictionaries of regular expressions (key:
-``pattern``) and replacements (key: ``repl``) in JSON format, which are
-applied in order by passing them to ``re.subn``. Transformation stops
-after the first match, so at most one transformation is applied. The general
-syntax is
-
-.. code:: python
-
- [{"pattern":"pattern1", "repl":"repl1"}, {"pattern":"pattern2", "repl":"repl2"}, ..., {"pattern":"patternN", "repl":"replN"}]
-
-The example below is for a Brother ADS-2400N, a scanner that allows
-different names to different hardware buttons (useful for handling
-multiple entities in one instance), but insists on adding ``_``
-to the filename.
-
-.. code:: python
-
- # Brother profile configuration, support "Name_Date_Count" (the default
- # setting) and "Name_Count" (use "Name" as tag and "Count" as title).
- PAPERLESS_FILENAME_PARSE_TRANSFORMS=[{"pattern":"^([a-z]+)_(\\d{8})_(\\d{6})_([0-9]+)\\.", "repl":"\\2\\3Z - \\4 - \\1."}, {"pattern":"^([a-z]+)_([0-9]+)\\.", "repl":" - \\2 - \\1."}]
-
-.. _guesswork-content:
-
-Reading the Document Contents
-=============================
-
-After the consumer has tried to figure out what it could from the file name,
-it starts looking at the content of the document itself. It will compare the
-matching algorithms defined by every tag and correspondent already set in your
-database to see if they apply to the text in that document. In other words,
-if you defined a tag called ``Home Utility`` that had a ``match`` property of
-``bc hydro`` and a ``matching_algorithm`` of ``literal``, Paperless will
-automatically tag your newly-consumed document with your ``Home Utility`` tag
-so long as the text ``bc hydro`` appears in the body of the document somewhere.
-
-The matching logic is quite powerful, and supports searching the text of your
-document with different algorithms, and as such, some experimentation may be
-necessary to get things Just Right.
-
-
-.. _guesswork-content-howto:
-
-How Do I Set Up These Matching Algorithms?
-------------------------------------------
-
-Setting up of the algorithms is easily done through the admin interface. When
-you create a new correspondent or tag, there are optional fields for matching
-text and matching algorithm. From the help info there:
-
-.. note::
-
- Which algorithm you want to use when matching text to the OCR'd PDF. Here,
- "any" looks for any occurrence of any word provided in the PDF, while "all"
- requires that every word provided appear in the PDF, albeit not in the
- order provided. A "literal" match means that the text you enter must
- appear in the PDF exactly as you've entered it, and "regular expression"
- uses a regex to match the PDF. If you don't know what a regex is, you
- probably don't want this option.
-
-When using the "any" or "all" matching algorithms, you can search for terms
-that consist of multiple words by enclosing them in double quotes. For example,
-defining a match text of ``"Bank of America" BofA`` using the "any" algorithm,
-will match documents that contain either "Bank of America" or "BofA", but will
-not match documents containing "Bank of South America".
-
-Then just save your tag/correspondent and run another document through the
-consumer. Once complete, you should see the newly-created document,
-automatically tagged with the appropriate data.
diff --git a/docs/index.rst b/docs/index.rst
index 5dfcaf471..a083fb3d1 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,17 +1,14 @@
-.. _index:
-
+*********
Paperless
-=========
+*********
Paperless is a simple Django application running in two parts:
-a :ref:`consumer ` (the thing that does the indexing) and
-the :ref:`webserver ` (the part that lets you search &
+a *Consumer* (the thing that does the indexing) and
+the *Web server* (the part that lets you search &
download already-indexed documents). If you want to learn more about its
functions keep on reading after the installation section.
-.. _index-why-this-exists:
-
Why This Exists
===============
@@ -25,22 +22,54 @@ finding stuff again. I feed documents right from the post box into the scanner
and then shred them. Perhaps you might find it useful too.
+Paperless-ng
+============
+
+Paperless-ng is a fork of the original paperless project. It changes many
+things both on the surface and under the hood. Paperless-ng was created
+because I feel that these changes are too big to be pushed into the main
+repository right away.
+
+NG stands for both Angular (the framework used for the
+Frontend) and next-gen. Publishing this project under a different name also
+avoids confusion between paperless and paperless-ng.
+
+If you want to learn about what's different in paperless-ng, check out these
+resources in the documentation:
+
+* :ref:`Some screenshots ` of the new UI are available.
+* Read :ref:`this section ` if you want to
+ learn about how paperless automates all tagging using machine learning.
+* Paperless now comes with a :ref:`proper email consumer `
+ that's fully tested and production ready.
+* Paperless creates searchable PDF/A documents from whatever you you put into
+ the consumption directory. This means that you can select text in
+ image-only documents coming from your scanner.
+* See :ref:`this note ` about GnuPG encryption in
+ paperless-ng.
+* Paperless is now integrated with a
+ :ref:`task processing queue ` that tells you
+ at a glance when and why something is not working.
+* The :ref:`changelog ` contains a detailed list of all changes
+ in paperless-ng.
+
+It would be great if this project could eventually merge back into the main
+repository, but it needs a lot more work before that can happen.
Contents
========
.. toctree::
- :maxdepth: 2
+ :maxdepth: 1
- requirements
setup
- consumption
+ usage_overview
+ advanced_usage
+ administration
+ configuration
api
- utilities
- guesswork
- migrating
- customising
+ faq
extending
troubleshooting
contributing
diff --git a/docs/migrating.rst b/docs/migrating.rst
deleted file mode 100644
index c3e702bd5..000000000
--- a/docs/migrating.rst
+++ /dev/null
@@ -1,109 +0,0 @@
-.. _migrating:
-
-Migrating, Updates, and Backups
-===============================
-
-As Paperless is still under active development, there's a lot that can change
-as software updates roll out. You should backup often, so if anything goes
-wrong during an update, you at least have a means of restoring to something
-usable. Thankfully, there are automated ways of backing up, restoring, and
-updating the software.
-
-
-.. _migrating-backup:
-
-Backing Up
-----------
-
-So you're bored of this whole project, or you want to make a remote backup of
-your files for whatever reason. This is easy to do, simply use the
-:ref:`exporter ` to dump your documents and database out
-into an arbitrary directory.
-
-
-.. _migrating-restoring:
-
-Restoring
----------
-
-Restoring your data is just as easy, since nearly all of your data exists either
-in the file names, or in the contents of the files themselves. You just need to
-create an empty database (just follow the
-:ref:`installation instructions ` again) and then import the
-``tags.json`` file you created as part of your backup. Lastly, copy your
-exported documents into the consumption directory and start up the consumer.
-
-.. code-block:: shell-session
-
- $ cd /path/to/project
- $ rm data/db.sqlite3 # Delete the database
- $ cd src
- $ ./manage.py migrate # Create the database
- $ ./manage.py createsuperuser
- $ ./manage.py loaddata /path/to/arbitrary/place/tags.json
- $ cp /path/to/exported/docs/* /path/to/consumption/dir/
- $ ./manage.py document_consumer
-
-Importing your data if you are :ref:`using Docker `
-is almost as simple:
-
-.. code-block:: shell-session
-
- # Stop and remove your current containers
- $ docker-compose stop
- $ docker-compose rm -f
-
- # Recreate them, add the superuser
- $ docker-compose up -d
- $ docker-compose run --rm webserver createsuperuser
-
- # Load the tags
- $ cat /path/to/arbitrary/place/tags.json | docker-compose run --rm webserver loaddata_stdin -
-
- # Load your exported documents into the consumption directory
- # (How you do this highly depends on how you have set this up)
- $ cp /path/to/exported/docs/* /path/to/mounted/consumption/dir/
-
-After loading the documents into the consumption directory the consumer will
-immediately start consuming the documents.
-
-
-.. _migrating-updates:
-
-Updates
--------
-
-For the most part, all you have to do to update Paperless is run ``git pull``
-on the directory containing the project files, and then use Django's
-``migrate`` command to execute any database schema updates that might have been
-rolled in as part of the update:
-
-.. code-block:: shell-session
-
- $ cd /path/to/project
- $ git pull
- $ pip install -r requirements.txt
- $ cd src
- $ ./manage.py migrate
-
-Note that it's possible (even likely) that while ``git pull`` may update some
-files, the ``migrate`` step may not update anything. This is totally normal.
-
-Additionally, as new features are added, the ability to control those features
-is typically added by way of an environment variable set in ``paperless.conf``.
-You may want to take a look at the ``paperless.conf.example`` file to see if
-there's anything new in there compared to what you've got in ``/etc``.
-
-If you are :ref:`using Docker ` the update process
-is similar:
-
-.. code-block:: shell-session
-
- $ cd /path/to/project
- $ git pull
- $ docker build -t paperless .
- $ docker-compose run --rm consumer migrate
- $ docker-compose up -d
-
-If ``git pull`` doesn't report any changes, there is no need to continue with
-the remaining steps.
diff --git a/docs/requirements.rst b/docs/requirements.rst
deleted file mode 100644
index 54f0d9216..000000000
--- a/docs/requirements.rst
+++ /dev/null
@@ -1,125 +0,0 @@
-.. _requirements:
-
-Requirements
-============
-
-You need a Linux machine or Unix-like setup (theoretically an Apple machine
-should work) that has the following software installed:
-
-* `Python3`_ (with development libraries, pip and virtualenv)
-* `GNU Privacy Guard`_
-* `Tesseract`_, plus its language files matching your document base.
-* `Imagemagick`_ version 6.7.5 or higher
-* `unpaper`_
-* `libpoppler-cpp-dev`_ PDF rendering library
-* `optipng`_
-
-.. _Python3: https://python.org/
-.. _GNU Privacy Guard: https://gnupg.org
-.. _Tesseract: https://github.com/tesseract-ocr
-.. _Imagemagick: http://imagemagick.org/
-.. _unpaper: https://github.com/unpaper/unpaper
-.. _libpoppler-cpp-dev: https://poppler.freedesktop.org/
-.. _optipng: http://optipng.sourceforge.net/
-
-Notably, you should confirm how you access your Python3 installation. Many
-Linux distributions will install Python3 in parallel to Python2, using the
-names ``python3`` and ``python`` respectively. The same goes for ``pip3`` and
-``pip``. Running Paperless with Python2 will likely break things, so make sure
-that you're using the right version.
-
-For the purposes of simplicity, ``python`` and ``pip`` is used everywhere to
-refer to their Python3 versions.
-
-In addition to the above, there are a number of Python requirements, all of
-which are listed in a file called ``requirements.txt`` in the project root
-directory.
-
-If you're not working on a virtual environment (like Docker), you
-should probably be using a virtualenv, but that's your call. The reasons why
-you might choose a virtualenv or not aren't really within the scope of this
-document. Needless to say if you don't know what a virtualenv is, you should
-probably figure that out before continuing.
-
-
-.. _requirements-apple:
-
-Problems with Imagemagick & PDFs
---------------------------------
-
-Some users have `run into problems`_ with getting ImageMagick to do its thing
-with PDFs. Often this is the case with Apple systems using HomeBrew, but other
-Linuxes have been a problem as well. The solution appears to be to install
-ghostscript as well as ImageMagick:
-
-.. _run into problems: https://github.com/the-paperless-project/paperless/issues/25
-
-.. code:: bash
-
- $ brew install ghostscript
- $ brew install imagemagick
- $ brew install libmagic
-
-
-.. _requirements-baremetal:
-
-Python-specific Requirements: No Virtualenv
--------------------------------------------
-
-If you don't care to use a virtual env, then installation of the Python
-dependencies is easy:
-
-.. code:: bash
-
- $ pip install --user --requirement /path/to/paperless/requirements.txt
-
-This will download and install all of the requirements into
-``${HOME}/.local``. Remember that your distribution may be using ``pip3`` as
-mentioned above.
-
-
-.. _requirements-virtualenv:
-
-Python-specific Requirements: Virtualenv
-----------------------------------------
-
-Using a virtualenv for this is pretty straightforward: create a virtualenv,
-enter it, and install the requirements using the ``requirements.txt`` file:
-
-.. code:: bash
-
- $ virtualenv --python=/path/to/python3 /path/to/arbitrary/directory
- $ . /path/to/arbitrary/directory/bin/activate
- $ pip install --requirement /path/to/paperless/requirements.txt
-
-Now you're ready to go. Just remember to enter (activate) your virtualenv
-whenever you want to use Paperless.
-
-
-.. _requirements-documentation:
-
-Documentation
--------------
-
-As generation of the documentation is not required for the use of Paperless,
-dependencies for this process are not included in ``requirements.txt``. If
-you'd like to generate your own docs locally, you'll need to:
-
-.. code:: bash
-
- $ pip install sphinx
-
-and then cd into the ``docs`` directory and type ``make html``.
-
-If you are using Docker, you can use the following commands to build the
-documentation and run a webserver serving it on `port 8001`_:
-
-.. code:: bash
-
- $ pwd
- /path/to/paperless
-
- $ docker build -t paperless:docs -f docs/Dockerfile .
- $ docker run --rm -it -p "8001:8000" paperless:docs
-
-.. _port 8001: http://127.0.0.1:8001
diff --git a/docs/scanners.rst b/docs/scanners.rst
index 70d71c962..5551fe55d 100644
--- a/docs/scanners.rst
+++ b/docs/scanners.rst
@@ -1,12 +1,14 @@
+
.. _scanners:
-Scanner Recommendations
-=======================
+***********************
+Scanner recommendations
+***********************
As Paperless operates by watching a folder for new files, doesn't care what
scanner you use, but sometimes finding a scanner that will write to an FTP,
NFS, or SMB server can be difficult. This page is here to help you find one
-that works right for you based on recommentations from other Paperless users.
+that works right for you based on recommendations from other Paperless users.
+---------+----------------+-----+-----+-----+----------------+
| Brand | Model | Supports | Recommended By |
@@ -25,6 +27,8 @@ that works right for you based on recommentations from other Paperless users.
+---------+----------------+-----+-----+-----+----------------+
| Epson | `WF-7710DWF`_ | yes | | yes | `Skylinar`_ |
+---------+----------------+-----+-----+-----+----------------+
+| Fujitsu | `S1300i`_ | yes | | yes | `jonaswinkler`_|
++---------+----------------+-----+-----+-----+----------------+
.. _ADS-1500W: https://www.brother.ca/en/p/ads1500w
.. _MFC-J6930DW: https://www.brother.ca/en/p/MFCJ6930DW
@@ -32,6 +36,7 @@ that works right for you based on recommentations from other Paperless users.
.. _MFC-9142CDN: https://www.brother.co.uk/printers/laser-printers/mfc9140cdn
.. _ix500: http://www.fujitsu.com/us/products/computing/peripheral/scanners/scansnap/ix500/
.. _WF-7710DWF: https://www.epson.de/en/products/printers/inkjet-printers/for-home/workforce-wf-7710dwf
+.. _S1300i: https://www.fujitsu.com/global/products/computing/peripheral/scanners/soho/s1300i/
.. _danielquinn: https://github.com/danielquinn
.. _ayounggun: https://github.com/ayounggun
@@ -39,3 +44,4 @@ that works right for you based on recommentations from other Paperless users.
.. _eonist: https://github.com/eonist
.. _REOLDEV: https://github.com/REOLDEV
.. _Skylinar: https://github.com/Skylinar
+.. _jonaswinkler: https://github.com/jonaswinkler
diff --git a/docs/screenshots.rst b/docs/screenshots.rst
index 53f564dd6..7ba431563 100644
--- a/docs/screenshots.rst
+++ b/docs/screenshots.rst
@@ -1,16 +1,45 @@
.. _screenshots:
+***********
Screenshots
-===========
+***********
-Once everything is set-up login to paperless using the web front-end
+This is what paperless-ng looks like. You shouldn't use paperless to index
+research papers though, its a horrible tool for that job.
-.. image:: ./_static/Screenshot_first_run_login.png
+The dashboard shows customizable views on your document and allows document uploads:
-Nice clean interface
+.. image:: _static/screenshots/dashboard.png
-.. image:: ./_static/Screenshot_first_logged.png
+The document list provides three different styles to scroll through your documents:
-Some documents loaded in via ftp or using the scanners ftp.
+.. image:: _static/screenshots/documents-table.png
+.. image:: _static/screenshots/documents-smallcards.png
+.. image:: _static/screenshots/documents-largecards.png
+
+Extensive filtering mechanisms:
+
+.. image:: _static/screenshots/documents-filter.png
+
+Side-by-side editing of documents. Optimized for 1080p.
+
+.. image:: _static/screenshots/editing.png
+
+Tag editing. This looks about the same for correspondents and document types.
+
+.. image:: _static/screenshots/new-tag.png
+
+Searching provides auto complete and highlights the results.
+
+.. image:: _static/screenshots/search-preview.png
+.. image:: _static/screenshots/search-results.png
+
+Fancy mail filters!
+
+.. image:: _static/screenshots/mail-rules-edited.png
+
+Mobile support in the future? This kinda works, however some layouts are still
+too wide.
+
+.. image:: _static/screenshots/mobile.png
-.. image:: ./_static/Screenshot_upload_and_scanned.png
diff --git a/docs/setup.rst b/docs/setup.rst
index 59f408015..e20b2e54a 100644
--- a/docs/setup.rst
+++ b/docs/setup.rst
@@ -1,542 +1,596 @@
-.. _setup:
+*****
Setup
-=====
-
-Paperless isn't a very complicated app, but there are a few components, so some
-basic documentation is in order. If you follow along in this document and
-still have trouble, please open an `issue on GitHub`_ so I can fill in the
-gaps.
-
-.. _issue on GitHub: https://github.com/the-paperless-project/paperless/issues
-
-
-.. _setup-download:
+*****
Download
---------
+########
-The source is currently only available via GitHub, so grab it from there,
-either by using ``git``:
+Go to the project page on GitHub and download the
+`latest release `_.
+There are multiple options available.
-.. code:: bash
+* Download the dockerfiles archive if you want to pull paperless from
+ Docker Hub.
- $ git clone https://github.com/the-paperless-project/paperless.git
- $ cd paperless
+* Download the dist archive and extract it if you want to build the docker image
+ yourself or want to install paperless without docker.
-or just download the tarball and go that route:
+.. hint::
-.. code:: bash
+ In contrast to paperless, the recommended way to get and update paperless-ng
+ is not to pull the entire git repository. Paperless-ng includes artifacts
+ that need to be compiled, and that's already done for you in the release.
- $ cd to the directory where you want to run Paperless
- $ wget https://github.com/the-paperless-project/paperless/archive/master.zip
- $ unzip master.zip
- $ cd paperless-master
+.. admonition:: Want to try out paperless-ng before migrating?
+
+ The release contains a file ``.env`` which sets the docker-compose project
+ name to "paperless", which is the same as before and instructs docker-compose
+ to reuse and upgrade your paperless volumes.
+
+ Just rename the project name in that file to anything else and docker-compose
+ will create fresh volumes for you!
-.. _setup-installation:
+Overview of Paperless-ng
+########################
-Installation & Configuration
-----------------------------
+Compared to paperless, paperless-ng works a little different under the hood and has
+more moving parts that work together. While this increases the complexity of
+the system, it also brings many benefits.
+
+Paperless consists of the following components:
+
+* **The webserver:** This is pretty much the same as in paperless. It serves
+ the administration pages, the API, and the new frontend. This is the main
+ tool you'll be using to interact with paperless. You may start the webserver
+ with
+
+ .. code:: shell-session
+
+ $ cd /path/to/paperless/src/
+ $ pipenv run gunicorn -c /usr/src/paperless/gunicorn.conf.py -b 0.0.0.0:8000 paperless.wsgi
+
+ or by any other means such as Apache ``mod_wsgi``.
+
+* **The consumer:** This is what watches your consumption folder for documents.
+ However, the consumer itself does not consume really consume your documents anymore.
+ It rather notifies a task processor that a new file is ready for consumption.
+ I suppose it should be named differently.
+ This also used to check your emails, but that's now gone elsewhere as well.
+
+ Start the consumer with the management command ``document_consumer``:
+
+ .. code:: shell-session
+
+ $ cd /path/to/paperless/src/
+ $ pipenv run python3 manage.py document_consumer
+
+ .. _setup-task_processor:
+
+* **The task processor:** Paperless relies on `Django Q `_
+ for doing much of the heavy lifting. This is a task queue that accepts tasks from
+ multiple sources and processes tasks in parallel. It also comes with a scheduler that executes
+ certain commands periodically.
+
+ This task processor is responsible for:
+
+ * Consuming documents. When the consumer finds new documents, it notifies the task processor to
+ start a consumption task.
+ * Consuming emails. It periodically checks your configured accounts for new mails and
+ produces consumption tasks for any documents it finds.
+ * The task processor also performs the consumption of any documents you upload through
+ the web interface.
+ * Maintain the search index and the automatic matching algorithm. These are things that paperless
+ needs to do from time to time in order to operate properly.
+
+ This allows paperless to process multiple documents from your consumption folder in parallel! On
+ a modern multi core system, consumption with full ocr is blazing fast.
+
+ The task processor comes with a built-in admin interface that you can use to see whenever any of the
+ tasks fail and inspect the errors (i.e., wrong email credentials, errors during consuming a specific
+ file, etc).
+
+ You may start the task processor by executing:
+
+ .. code:: shell-session
+
+ $ cd /path/to/paperless/src/
+ $ pipenv run python3 manage.py qcluster
+
+* A `redis `_ message broker: This is a really lightweight service that is responsible
+ for getting the tasks from the webserver and consumer to the task scheduler. These run in different
+ processes (maybe even on different machines!), and therefore, this is necessary.
+
+* Optional: A database server. Paperless supports both PostgreSQL and SQLite for storing its data.
+
+
+Installation
+############
You can go multiple routes with setting up and running Paperless:
- * The `bare metal route`_
- * The `docker route`_
- * A suggested `linux containers route`_
+* The `docker route`_
+* The `bare metal route`_
+
+The `docker route`_ is quick & easy. This is the recommended route. This configures all the stuff
+from above automatically so that it just works and uses sensible defaults for all configuration options.
+
+The `bare metal route`_ is more complicated to setup but makes it easier
+should you want to contribute some code back. You need to configure and
+run the above mentioned components yourself.
+
+Docker Route
+============
+
+1. Install `Docker`_ and `docker-compose`_. [#compose]_
+
+ .. caution::
+
+ If you want to use the included ``docker-compose.*.yml`` file, you
+ need to have at least Docker version **17.09.0** and docker-compose
+ version **1.17.0**.
+
+ See the `Docker installation guide`_ on how to install the current
+ version of Docker for your operating system or Linux distribution of
+ choice. To get an up-to-date version of docker-compose, follow the
+ `docker-compose installation guide`_ if your package repository doesn't
+ include it.
+
+ .. _Docker installation guide: https://docs.docker.com/engine/installation/
+ .. _docker-compose installation guide: https://docs.docker.com/compose/install/
+
+2. Copy either ``docker-compose.sqlite.yml`` or ``docker-compose.postgres.yml`` to
+ ``docker-compose.yml``, depending on which database backend you want to use.
+
+ .. hint::
+
+ For new installations, it is recommended to use PostgreSQL as the database
+ backend.
+
+2. Modify ``docker-compose.yml`` to your preferences. You may want to change the path
+ to the consumption directory in this file. Find the line that specifies where
+ to mount the consumption directory:
+
+ .. code::
+
+ - ./consume:/usr/src/paperless/consume
+
+ Replace the part BEFORE the colon with a local directory of your choice:
+
+ .. code::
+
+ - /home/jonaswinkler/paperless-inbox:/usr/src/paperless/consume
+
+ Don't change the part after the colon or paperless wont find your documents.
-The `docker route`_ is quick & easy.
+3. Modify ``docker-compose.env``, following the comments in the file. The
+ most important change is to set ``USERMAP_UID`` and ``USERMAP_GID``
+ to the uid and gid of your user on the host system. This ensures that
+ both the docker container and you on the host machine have write access
+ to the consumption directory. If your UID and GID on the host system is
+ 1000 (the default for the first normal user on most systems), it will
+ work out of the box without any modifications.
-The `bare metal route`_ is a bit more complicated to setup but makes it easier
-should you want to contribute some code back.
+ .. note::
-The `linux containers route`_ is quick, but makes alot of assumptions on the
-set-up, on the other hand the script could be used to install on a base
-debian or ubuntu server.
+ You can use any settings from the file ``paperless.conf`` in this file.
+ Have a look at :ref:`configuration` to see whats available.
-.. _docker route: setup-installation-docker_
-.. _bare metal route: setup-installation-bare-metal_
-.. _Docker Machine: https://docs.docker.com/machine/
-.. _linux containers route: setup-installation-linux-containers_
+4. Run ``docker-compose up -d``. This will create and start the necessary
+ containers. This will also build the image of paperless if you grabbed the
+ source archive.
-.. _setup-installation-bare-metal:
+5. To be able to login, you will need a super user. To create it, execute the
+ following command:
-Standard (Bare Metal)
-+++++++++++++++++++++
+ .. code-block:: shell-session
-1. Install the requirements as per the :ref:`requirements ` page.
-2. Within the extract of master.zip go to the ``src`` directory.
-3. Copy ``../paperless.conf.example`` to ``/etc/paperless.conf`` and open it in
- your favourite editor. As this file contains passwords. It should only be
- readable by user root and paperless! Set the values for:
+ $ docker-compose run --rm webserver createsuperuser
- Set the values for:
-
- * ``PAPERLESS_CONSUMPTION_DIR``: this is where your documents will be
- dumped to be consumed by Paperless.
- * ``PAPERLESS_OCR_THREADS``: this is the number of threads the OCR process
- will spawn to process document pages in parallel.
- * ``PAPERLESS_PASSPHRASE``: this is only required if you want to use GPG to
- encrypt your document files. This is the passphrase Paperless uses to
- encrypt/decrypt the original documents. Don't worry about defining this
- if you don't want to use encryption (the default).
-
- Note also that if you're using the ``runserver`` as mentioned below, you
- should make sure that PAPERLESS_DEBUG="true" or is just commented out as
- this is the default.
-
-4. Initialise the SQLite database with ``./manage.py migrate``.
-5. Collect the static files for the webserver with ``./manage.py collectstatic``.
-6. Create a user for your Paperless instance with
- ``./manage.py createsuperuser``. Follow the prompts to create your user.
-7. Start the webserver with ``./manage.py runserver :``.
- If no specific IP or port is given, the default is ``127.0.0.1:8000`` also
- known as http://localhost:8000/.
- You should now be able to visit your (empty) installation at
- `Paperless webserver`_ or whatever you chose before. You can login with the
- user/pass you created in #5.
-
-8. In a separate window, change to the ``src`` directory in this repo again,
- but this time, you should start the consumer script with
- ``./manage.py document_consumer``.
-9. Scan something or put a file into the ``CONSUMPTION_DIR``.
-10. Wait a few minutes
-11. Visit the document list on your webserver, and it should be there, indexed
- and downloadable.
-
-.. caution::
-
- This installation is not secure. Once everything is working head over to
- `Making things more permanent`_
-
-.. _Paperless webserver: http://127.0.0.1:8000
-.. _Making things more permanent: setup-permanent_
-
-.. _setup-installation-docker:
-
-Docker Method
-+++++++++++++
-
-1. Install `Docker`_.
-
- .. caution::
-
- As mentioned earlier, this guide assumes that you use Docker natively
- under Linux. If you are using `Docker Machine`_ under Mac OS X or
- Windows, you will have to adapt IP addresses, volume-mounting, command
- execution and maybe more.
-
-2. Install `docker-compose`_. [#compose]_
-
- .. caution::
-
- If you want to use the included ``docker-compose.yml.example`` file, you
- need to have at least Docker version **1.12.0** and docker-compose
- version **1.9.0**.
-
- See the `Docker installation guide`_ on how to install the current
- version of Docker for your operating system or Linux distribution of
- choice. To get an up-to-date version of docker-compose, follow the
- `docker-compose installation guide`_ if your package repository doesn't
- include it.
-
- .. _Docker installation guide: https://docs.docker.com/engine/installation/
- .. _docker-compose installation guide: https://docs.docker.com/compose/install/
-
-3. Create a copy of ``docker-compose.yml.example`` as ``docker-compose.yml``
- and a copy of ``docker-compose.env.example`` as ``docker-compose.env``.
- You'll be editing both these files: taking a copy ensures that you can
- ``git pull`` to receive updates without risking merge conflicts with your
- modified versions of the configuration files.
-4. Modify ``docker-compose.yml`` to your preferences, following the
- instructions in comments in the file. The only change that is a hard
- requirement is to specify where the consumption directory should
- mount.[#dockercomposeyml]_
-
- .. caution::
-
- If you are using NFS mounts for the consume directory you also need to
- change the command to turn off inotify as it doesn't work with NFS
-
- ``command: ["document_consumer", "--no-inotify"]``
-
-
-5. Modify ``docker-compose.env`` and adapt the following environment variables:
-
- ``PAPERLESS_PASSPHRASE``
- This is the passphrase Paperless uses to encrypt/decrypt the original
- document. If you aren't planning on using GPG encryption, you can just
- leave this undefined.
-
- ``PAPERLESS_OCR_THREADS``
- This is the number of threads the OCR process will spawn to process
- document pages in parallel. If the variable is not set, Python determines
- the core-count of your CPU and uses that value.
-
- ``PAPERLESS_OCR_LANGUAGES``
- If you want the OCR to recognize other languages in addition to the
- default English, set this parameter to a space separated list of
- three-letter language-codes after `ISO 639-2/T`_. For a list of available
- languages -- including their three letter codes -- see the
- `Alpine packagelist`_.
-
- ``USERMAP_UID`` and ``USERMAP_GID``
- If you want to mount the consumption volume (directory ``/consume`` within
- the containers) to a host-directory -- which you probably want to do --
- access rights might be an issue. The default user and group ``paperless``
- in the containers have an id of 1000. The containers will enforce that the
- owning group of the consumption directory will be ``paperless`` to be able
- to delete consumed documents. If your host-system has a group with an ID
- of 1000 and you don't want this group to have access rights to the
- consumption directory, you can use ``USERMAP_GID`` to change the id in the
- container and thus the one of the consumption directory. Furthermore, you
- can change the id of the default user as well using ``USERMAP_UID``.
-
- ``PAPERLESS_USE_SSL``
- If you want Paperless to use SSL for the user interface, set this variable
- to ``true``. You also need to copy your certificate and key to the ``data``
- directory, named ``ssl.cert`` and ``ssl.key``.
- This is not an ideal solution and, if possible, a reverse proxy with nginx
- is preferred.
-
-6. Run ``docker-compose up -d``. This will create and start the necessary
- containers.
-7. To be able to login, you will need a super user. To create it, execute the
- following command:
-
- .. code-block:: shell-session
-
- $ docker-compose run --rm webserver createsuperuser
-
- This will prompt you to set a username (default ``paperless``), an optional
- e-mail address and finally a password.
-8. The default ``docker-compose.yml`` exports the webserver on your local port
- 8000. If you haven't adapted this, you should now be able to visit your
- `Paperless webserver`_ at ``http://127.0.0.1:8000`` (or
- ``https://127.0.0.1:8000`` if you enabled SSL). You can login with the
- user and password you just created.
-9. Add files to consumption directory the way you prefer to. Following are two
- possible options:
-
- 1. Mount the consumption directory to a local host path by modifying your
- ``docker-compose.yml``:
-
- .. code-block:: diff
-
- diff --git a/docker-compose.yml b/docker-compose.yml
- --- a/docker-compose.yml
- +++ b/docker-compose.yml
- @@ -17,9 +18,8 @@ services:
- volumes:
- - paperless-data:/usr/src/paperless/data
- - paperless-media:/usr/src/paperless/media
- - - /consume
- + - /local/path/you/choose:/consume
-
- .. danger::
-
- While the consumption container will ensure at startup that it can
- **delete** a consumed file from a host-mounted directory, it might
- not be able to **read** the document in the first place if the access
- rights to the file are incorrect.
-
- Make sure that the documents you put into the consumption directory
- will either be readable by everyone (``chmod o+r file.pdf``) or
- readable by the default user or group id 1000 (or the one you have
- set with ``USERMAP_UID`` or ``USERMAP_GID`` respectively).
-
- 2. Use ``docker cp`` to copy your files directly into the container:
-
- .. code-block:: shell-session
-
- $ # Identify your containers
- $ docker-compose ps
- Name Command State Ports
- -------------------------------------------------------------------------
- paperless_consumer_1 /sbin/docker-entrypoint.sh ... Exit 0
- paperless_webserver_1 /sbin/docker-entrypoint.sh ... Exit 0
-
- $ docker cp /path/to/your/file.pdf paperless_consumer_1:/consume
-
- ``docker cp`` is a one-shot-command, just like ``cp``. This means that
- every time you want to consume a new document, you will have to execute
- ``docker cp`` again. You can of course automate this process, but option
- 1 is generally the preferred one.
-
- .. danger::
-
- ``docker cp`` will change the owning user and group of a copied file
- to the acting user at the destination, which will be ``root``.
-
- You therefore need to ensure that the documents you want to copy into
- the container are readable by everyone (``chmod o+r file.pdf``)
- before copying them.
+ This will prompt you to set a username, an optional e-mail address and
+ finally a password.
+6. The default ``docker-compose.yml`` exports the webserver on your local port
+ 8000. If you haven't adapted this, you should now be able to visit your
+ Paperless instance at ``http://127.0.0.1:8000``. You can login with the
+ user and password you just created.
.. _Docker: https://www.docker.com/
.. _docker-compose: https://docs.docker.com/compose/install/
-.. _ISO 639-2/T: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
-.. _Alpine packagelist: https://pkgs.alpinelinux.org/packages?name=tesseract-ocr-data*&arch=x86_64
.. [#compose] You of course don't have to use docker-compose, but it
simplifies deployment immensely. If you know your way around Docker, feel
free to tinker around without using compose!
-.. [#dockercomposeyml] If you're upgrading your docker-compose images from
- version 1.1.0 or earlier, you might need to change in the
- ``docker-compose.yml`` file the ``image: pitkley/paperless`` directive in
- both the ``webserver`` and ``consumer`` sections to ``build: ./`` as per the
- newer ``docker-compose.yml.example`` file
+.. _`setup-bare_metal`:
+Bare Metal Route
+================
-.. _setup-permanent:
+Paperless runs on linux only. The following procedure has been tested on a minimal
+installation of Debian/Buster, which is the current stable release at the time of
+writing. Windows is not and will never be supported.
-Making Things a Little more Permanent
--------------------------------------
+1. Install dependencies. Paperless requires the following packages.
-Once you've tested things and are happy with the work flow, you should secure
-the installation and automate the process of starting the webserver and
-consumer.
+ * ``python3`` 3.6, 3.7, 3.8 (3.9 is untested).
+ * ``python3-pip``, optionally ``pipenv`` for package installation
+ * ``python3-dev``
+ * ``imagemagick`` >= 6 for PDF conversion
+ * ``optipng`` for optimising thumbnails
+ * ``gnupg`` for handling encrypted documents
+ * ``libpoppler-cpp-dev`` for PDF to text conversion
+ * ``libmagic-dev`` for mime type detection
+ * ``libpq-dev`` for PostgreSQL
-.. _setup-permanent-webserver:
+ These dependencies are required for OCRmyPDF, which is used for text recognition.
-Using a Real Webserver
-++++++++++++++++++++++
+ * ``unpaper``
+ * ``ghostscript``
+ * ``icc-profiles-free``
+ * ``qpdf``
+ * ``liblept5``
+ * ``libxml2``
+ * ``pngquant``
+ * ``zlib1g``
+ * ``tesseract-ocr`` >= 4.0.0 for OCR
+ * ``tesseract-ocr`` language packs (``tesseract-ocr-eng``, ``tesseract-ocr-deu``, etc)
-The default is to use Django's development server, as that's easy and does the
-job well enough on a home network. However it is heavily discouraged to use
-it for more than that.
+ You will also need ``build-essential``, ``python3-setuptools`` and ``python3-wheel``
+ for installing some of the python dependencies. You can remove that
+ again after installation.
-If you want to do things right you should use a real webserver capable of
-handling more than one thread. You will also have to let the webserver serve
-the static files (CSS, JavaScript) from the directory configured in
-``PAPERLESS_STATICDIR``. The default static files directory is ``../static``.
+2. Install ``redis`` >= 5.0 and configure it to start automatically.
-For that you need to activate your virtual environment and collect the static
-files with the command:
+3. Optional. Install ``postgresql`` and configure a database, user and password for paperless. If you do not wish
+ to use PostgreSQL, SQLite is avialable as well.
-.. code:: bash
+4. Get the release archive. If you pull the git repo as it is, you also have to compile the front end by yourself.
+ Extract the frontend to a place from where you wish to execute it, such as ``/opt/paperless``.
- $ cd /src
- $ ./manage.py collectstatic
+5. Configure paperless. See :ref:`configuration` for details. Edit the included ``paperless.conf`` and adjust the
+ settings to your needs. Required settings for getting paperless running are:
+ * ``PAPERLESS_REDIS`` should point to your redis server, such as redis://localhost:6379.
+ * ``PAPERLESS_DBHOST`` should be the hostname on which your PostgreSQL server is running. Do not configure this
+ to use SQLite instead. Also configure port, database name, user and password as necessary.
+ * ``PAPERLESS_CONSUMPTION_DIR`` should point to a folder which paperless should watch for documents. You might
+ want to have this somewhere else. Likewise, ``PAPERLESS_DATA_DIR`` and ``PAPERLESS_MEDIA_ROOT`` define where
+ paperless stores its data. If you like, you can point both to the same directory.
+ * ``PAPERLESS_SECRET_KEY`` should be a random sequence of characters. It's used for authentication. Failure
+ to do so allows third parties to forge authentication credentials.
+
+ Many more adjustments can be made to paperless, especially the OCR part. The following options are recommended
+ for everyone:
-Apache
-~~~~~~
+ * Set ``PAPERLESS_OCR_LANGUAGE`` to the language most of your documents are written in.
+ * Set ``PAPERLESS_TIME_ZONE`` to your local time zone.
-This is a configuration supplied by `steckerhalter`_ on GitHub. It uses Apache
-and mod_wsgi, with a Paperless installation in ``/home/paperless/``:
+6. Setup permissions. Create a system users under which you wish to run paperless. Ensure that these directories exist
+ and that the user has write permissions to the following directories
+
+ * ``/opt/paperless/media``
+ * ``/opt/paperless/data``
+ * ``/opt/paperless/consume``
-.. code:: apache
+ Adjust as necessary if you configured different folders.
-
- ServerName example.com
+7. Install python requirements. Paperless comes with both Pipfiles for ``pipenv`` as well as with a ``requirements.txt``.
+ Both will install exactly the same requirements. It is up to you if you wish to use a virtual environment or not.
- Alias /static/ /home/paperless/paperless/static/
-
- Require all granted
-
+8. Go to ``/opt/paperless/src``, and execute the following commands:
- WSGIScriptAlias / /home/paperless/paperless/src/paperless/wsgi.py
- WSGIDaemonProcess example.com user=paperless group=paperless threads=5 python-path=/home/paperless/paperless/src:/home/paperless/.env/lib/python3.6/site-packages
- WSGIProcessGroup example.com
+ .. code:: bash
-
-
- Require all granted
-
-
-
+ # This collects static files from paperless and django.
+ python3 manage.py collectstatic --clear --no-input
+
+ # This creates the database schema.
+ python3 manage.py migrate
-.. _steckerhalter: https://github.com/steckerhalter
+ # This creates your first paperless user
+ python3 manage.py createsuperuser
+9. Optional: Test that paperless is working by executing
-Nginx + Gunicorn
-~~~~~~~~~~~~~~~~
+ .. code:: bash
-If you're using Nginx, the most common setup is to combine it with a
-Python-based server like Gunicorn so that Nginx is acting as a proxy. Below is
-a copy of a simple Nginx configuration fragment making use of a gunicorn
-instance listening on localhost port 8000.
+ # This collects static files from paperless and django.
+ python3 manage.py runserver
+
+ and pointing your browser to http://localhost:8000/.
-.. code:: nginx
+ .. warning::
- server {
- listen 80;
+ This is a development server which should not be used in
+ production.
- index index.html index.htm index.php;
- access_log /var/log/nginx/paperless_access.log;
- error_log /var/log/nginx/paperless_error.log;
+ .. hint::
- location /static {
+ This will not start the consumer. Paperless does this in a
+ separate process.
- autoindex on;
- alias ;
+10. Setup systemd services to run paperless automatically. You may
+ use the service definition files included in the ``scripts`` folder
+ as a starting point.
- }
+ Paperless needs the ``webserver`` script to run the webserver, the
+ ``consumer`` script to watch the input folder, and the ``scheduler``
+ script to run tasks such as email checking and document consumption.
- location / {
+ These services rely on redis and optionally the database server, but
+ don't need to be started in any particular order. The example files
+ depend on redis being started. If you use a database server, you should
+ add additinal dependencies.
- proxy_set_header Host $http_host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
+ .. hint::
- proxy_pass http://127.0.0.1:8000;
- }
- }
+ You may optionally set up your preferred web server to serve
+ paperless as a wsgi application directly instead of running the
+ ``webserver`` service. The module containing the wsgi application
+ is named ``paperless.wsgi``.
+ .. caution::
-The gunicorn server can be started with the command:
+ The included scripts run a ``gunicorn`` standalone server,
+ which is fine for running paperless. It does support SSL,
+ however, the documentation of GUnicorn states that you should
+ use a proxy server in front of gunicorn instead.
-.. code-block:: shell
+11. Optional: Install a samba server and make the consumption folder
+ available as a network share.
- $ /bin/gunicorn --pythonpath=/src paperless.wsgi -w 2
+12. Configure ImageMagick to allow processing of PDF documents. Most distributions have
+ this disabled by default, since PDF documents can contain malware. If
+ you don't do this, paperless will fall back to ghostscript for certain steps
+ such as thumbnail generation.
+ Edit ``/etc/ImageMagick-6/policy.xml`` and adjust
-.. _setup-permanent-standard-systemd:
+ .. code::
-Standard (Bare Metal + Systemd)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+ to
-If you're running on a bare metal system that's using Systemd, you can use the
-service unit files in the ``scripts`` directory to set this up.
+ .. code::
-1. You'll need to create a group and user called ``paperless`` (without login)
-2. Setup Paperless to be in a place that this new user can read and write to.
-3. Ensure ``/etc/paperless`` is readable by the ``paperless`` user.
-4. Copy the service file from the ``scripts`` directory to
- ``/etc/systemd/system``.
+
-.. code-block:: bash
+Migration to paperless-ng
+#########################
- $ cp /path/to/paperless/scripts/paperless-consumer.service /etc/systemd/system/
- $ cp /path/to/paperless/scripts/paperless-webserver.service /etc/systemd/system/
+At its core, paperless-ng is still paperless and fully compatible. However, some
+things have changed under the hood, so you need to adapt your setup depending on
+how you installed paperless. The important things to keep in mind are as follows.
-5. Edit the service file to point the ``ExecStart`` line to the proper location
- of your paperless install, referencing the appropriate Python binary. For
- example:
- ``ExecStart=/path/to/python3 /path/to/paperless/src/manage.py document_consumer``.
-6. Start and enable (so they start on boot) the services.
+* Read the :ref:`changelog ` and take note of breaking changes.
+* You should decide if you want to stick with SQLite or want to migrate your database
+ to PostgreSQL. See :ref:`setup-sqlite_to_psql` for details on how to move your data from
+ SQLite to PostgreSQL. Both work fine with paperless. However, if you already have a
+ database server running for other services, you might as well use it for paperless as well.
+* The task scheduler of paperless, which is used to execute periodic tasks
+ such as email checking and maintenance, requires a `redis`_ message broker
+ instance. The docker-compose route takes care of that.
+* The layout of the folder structure for your documents and data remains the
+ same, so you can just plug your old docker volumes into paperless-ng and
+ expect it to find everything where it should be.
-.. code-block:: bash
+Migration to paperless-ng is then performed in a few simple steps:
- $ systemctl enable paperless-consumer
- $ systemctl enable paperless-webserver
- $ systemctl start paperless-consumer
- $ systemctl start paperless-webserver
+1. Stop paperless.
+ .. code:: bash
-.. _setup-permanent-standard-upstart:
+ $ cd /path/to/current/paperless
+ $ docker-compose down
-Standard (Bare Metal + Upstart)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+2. Do a backup for two purposes: If something goes wrong, you still have your
+ data. Second, if you don't like paperless-ng, you can switch back to
+ paperless.
-Ubuntu 14.04 and earlier use the `Upstart`_ init system to start services
-during the boot process. To configure Upstart to run Paperless automatically
-after restarting your system:
+3. Download the latest release of paperless-ng. You can either go with the
+ docker-compose files or use the archive to build the image yourself.
+ You can either replace your current paperless folder or put paperless-ng
+ in a different location.
-1. Change to the directory where Upstart's configuration files are kept:
- ``cd /etc/init``
-2. Create a new file: ``sudo nano paperless-server.conf``
-3. In the newly-created file enter::
+ .. caution::
- start on (local-filesystems and net-device-up IFACE=eth0)
- stop on shutdown
+ The release include a ``.env`` file. This will set the
+ project name for docker compose to ``paperless`` so that paperless-ng will
+ automatically reuse your existing paperless volumes. When you start it, it
+ will migrate your existing data. After that, your old paperless installation
+ will be incompatible with the migrated volumes.
- respawn
- respawn limit 10 5
+4. Copy the ``docker-compose.sqlite.yml`` file to ``docker-compose.yml``.
+ If you want to switch to PostgreSQL, do that after you migrated your existing
+ SQLite database.
- script
- exec /bin/gunicorn --pythonpath=/src paperless.wsgi -w 2
- end script
+5. Adjust ``docker-compose.yml`` and
+ ``docker-compose.env`` to your needs.
+ See `docker route`_ for details on which edits are advised.
- Note that you'll need to replace ``/srv/paperless/src/manage.py`` with the
- path to the ``manage.py`` script in your installation directory.
+6. Since ``docker-compose`` would just use the the old paperless image, we need to
+ manually build a new image:
- If you are using a network interface other than ``eth0``, you will have to
- change ``IFACE=eth0``. For example, if you are connected via WiFi, you will
- likely need to replace ``eth0`` above with ``wlan0``. To see all interfaces,
- run ``ifconfig -a``.
+ .. code:: shell-session
- Save the file.
+ $ docker-compose build
-4. Create a new file: ``sudo nano paperless-consumer.conf``
+7. In order to find your existing documents with the new search feature, you need
+ to invoke a one-time operation that will create the search index:
-5. In the newly-created file enter::
+ .. code:: shell-session
- start on (local-filesystems and net-device-up IFACE=eth0)
- stop on shutdown
+ $ docker-compose run --rm webserver document_index reindex
+
+ This will migrate your database and create the search index. After that,
+ paperless will take care of maintaining the index by itself.
- respawn
- respawn limit 10 5
+8. Start paperless-ng.
- script
- exec /bin/python /manage.py document_consumer
- end script
+ .. code:: bash
- Replace the path placeholder and ``eth0`` with the appropriate value and save the file.
+ $ docker-compose up -d
-These two configuration files together will start both the Paperless webserver
-and document consumer processes when the file system and network interface
-specified is available after boot. Furthermore, if either process ever exits
-unexpectedly, Upstart will try to restart it a maximum of 10 times within a 5
-second period.
+ This will run paperless in the background and automatically start it on system boot.
-.. _Upstart: http://upstart.ubuntu.com/
+9. Paperless installed a permanent redirect to ``admin/`` in your browser. This
+ redirect is still in place and prevents access to the new UI. Clear
+ browsing cache in order to fix this.
+10. Optionally, follow the instructions below to migrate your existing data to PostgreSQL.
-.. _setup-permanent-docker:
-Docker
-~~~~~~
+.. _setup-sqlite_to_psql:
-If you're using Docker, you can set a restart-policy_ in the
-``docker-compose.yml`` to have the containers automatically start with the
-Docker daemon.
+Moving data from SQLite to PostgreSQL
+=====================================
-.. _restart-policy: https://docs.docker.com/engine/reference/commandline/run/#restart-policies-restart
+Moving your data from SQLite to PostgreSQL is done via executing a series of django
+management commands as below.
+.. caution::
-.. _setup-installation-linux-containers:
+ Make sure that your SQLite database is migrated to the latest version.
+ Starting paperless will make sure that this is the case. If your try to
+ load data from an old database schema in SQLite into a newer database
+ schema in PostgreSQL, you will run into trouble.
-Suggested way for Linux Container Method
-++++++++++++++++++++++++++++++++++++++++
+.. warning::
-This method uses some rigid assumptions, for the best set-up:-
+ On some database fields, PostgreSQL enforces predefined limits on maximum
+ length, whereas SQLite does not. The fields in question are the title of documents
+ (128 characters), names of document types, tags and correspondents (128 characters),
+ and filenames (1024 characters). If you have data in these fields that surpasses these
+ limits, migration to PostgreSQL is not possible and will fail with an error.
- * Ubuntu lts as the container
- * Apache as the webserver
- * proftpd as ftp server
- * ftpupload as the ftp user
- * paperless as the main user for website
- * http://paperless.lan is the desired lan url
- * LXC set to give ip addresses on your lan
-This could also be used as an install on a base debain/ubuntu server,
-if the above assumptions are acceptable.
+1. Stop paperless, if it is running.
+2. Tell paperless to use PostgreSQL:
-1. Install lxc
+ a) With docker, copy the provided ``docker-compose.postgres.yml`` file to
+ ``docker-compose.yml``. Remember to adjust the consumption directory,
+ if necessary.
+ b) Without docker, configure the database in your ``paperless.conf`` file.
+ See :ref:`configuration` for details.
+3. Open a shell and initialize the database:
-2. Lanch paperless container
+ a) With docker, run the following command to open a shell within the paperless
+ container:
-.. code:: bash
+ .. code:: shell-session
- $ lxc launch ubuntu: paperless
+ $ cd /path/to/paperless
+ $ docker-compose run --rm webserver /bin/bash
+
+ This will launch the container and initialize the PostgreSQL database.
+
+ b) Without docker, open a shell in your virtual environment, switch to
+ the ``src`` directory and create the database schema:
-3. Run install script within container
+ .. code:: shell-session
-.. code:: bash
+ $ cd /path/to/paperless
+ $ pipenv shell
+ $ cd src
+ $ python3 manage.py migrate
+
+ This will not copy any data yet.
- $ lxc exec paperless -- sh -c "wget https://raw.githubusercontent.com/the-paperless-project/paperless/master/docs/examples/lxc/lxc-install.sh && /bin/bash lxc-install.sh --email"
+4. Dump your data from SQLite:
-The script will ask you for an ftpupload password.
-As well as the super-user for paperless web front-end.
-After around 10 mins, http://paperless.lan is ready and
-ftp://paperless.lan with user: ftpupload
+ .. code:: shell-session
-See the `Installation recording <_static/lxc-install.svg>`_.
+ $ python3 manage.py dumpdata --database=sqlite --exclude=contenttypes --exclude=auth.Permission > data.json
+
+5. Load your data into PostgreSQL:
+ .. code:: shell-session
+
+ $ python3 manage.py loaddata data.json
+
+6. Exit the shell.
+
+ .. code:: shell-session
+
+ $ exit
+
+7. Start paperless.
+
+
+Moving back to paperless
+========================
+
+Lets say you migrated to Paperless-ng and used it for a while, but decided that
+you don't like it and want to move back (If you do, send me a mail about what
+part you didn't like!), you can totally do that with a few simple steps.
+
+Paperless-ng modified the database schema slightly, however, these changes can
+be reverted while keeping your current data, so that your current data will
+be compatible with original Paperless.
+
+Execute this:
+
+.. code:: shell-session
+
+ $ cd /path/to/paperless
+ $ docker-compose run --rm webserver migrate documents 0023
+
+Or without docker:
+
+.. code:: shell-session
+
+ $ cd /path/to/paperless/src
+ $ python3 manage.py migrate documents 0023
+
+After that, you need to clear your cookies (Paperless-ng comes with updated
+dependencies that do cookie-processing differently) and probably your cache
+as well.
+
+.. _setup-less_powerful_devices:
+
+
+Considerations for less powerful devices
+########################################
+
+Paperless runs on Raspberry Pi. However, some things are rather slow on the Pi and
+configuring some options in paperless can help improve performance immensely:
+
+* Stick with SQLite to save some resources.
+* Consider setting ``PAPERLESS_OCR_PAGES`` to 1, so that paperless will only OCR
+ the first page of your documents.
+* ``PAPERLESS_TASK_WORKERS`` and ``PAPERLESS_THREADS_PER_WORKER`` are configured
+ to use all cores. The Raspberry Pi models 3 and up have 4 cores, meaning that
+ paperless will use 2 workers and 2 threads per worker. This may result in
+ sluggish response times during consumption, so you might want to lower these
+ settings (example: 2 workers and 1 thread to always have some computing power
+ left for other tasks).
+* Keep ``PAPERLESS_OCR_MODE`` at its default value ``skip`` and consider OCR'ing
+ your documents before feeding them into paperless. Some scanners are able to
+ do this! You might want to even specify ``skip_noarchive`` to skip archive
+ file generation for already ocr'ed documents entirely.
+* Set ``PAPERLESS_OPTIMIZE_THUMBNAILS`` to 'false' if you want faster consumption
+ times. Thumbnails will be about 20% larger.
+
+For details, refer to :ref:`configuration`.
+
+.. note::
+
+ Updating the :ref:`automatic matching algorithm `
+ takes quite a bit of time. However, the update mechanism checks if your
+ data has changed before doing the heavy lifting. If you experience the
+ algorithm taking too much cpu time, consider changing the schedule in the
+ admin interface to daily. You can also manually invoke the task
+ by changing the date and time of the next run to today/now.
+
+ The actual matching of the algorithm is fast and works on Raspberry Pi as
+ well as on any other device.
+
+
+
+.. _redis: https://redis.io/
diff --git a/docs/troubleshooting.rst b/docs/troubleshooting.rst
index 944977f1d..dc5bf7f5d 100644
--- a/docs/troubleshooting.rst
+++ b/docs/troubleshooting.rst
@@ -1,75 +1,51 @@
-.. _troubleshooting:
-
+***************
Troubleshooting
-===============
+***************
-.. _troubleshooting-languagemissing:
+No files are added by the consumer
+##################################
-Consumer warns ``OCR for XX failed``
-------------------------------------
+Check for the following issues:
-If you find the OCR accuracy to be too low, and/or the document consumer warns
-that ``OCR for XX failed, but we're going to stick with what we've got since
-FORGIVING_OCR is enabled``, then you might need to install the
-`Tesseract language files `_
-marching your document's languages.
+* Ensure that the directory you're putting your documents in is the folder
+ paperless is watching. With docker, this setting is performed in the
+ ``docker-compose.yml`` file. Without docker, look at the ``CONSUMPTION_DIR``
+ setting. Don't adjust this setting if you're using docker.
+* Ensure that redis is up and running. Paperless does its task processing
+ asynchronously, and for documents to arrive at the task processor, it needs
+ redis to run.
+* Ensure that the task processor is running. Docker does this automatically.
+ Manually invoke the task processor by executing
-As an example, if you are running Paperless from any Ubuntu or Debian
-box, and your documents are written in Spanish you may need to run::
+ .. code:: shell-session
- apt-get install -y tesseract-ocr-spa
+ $ python3 manage.py qcluster
+
+* Look at the output of paperless and inspect it for any errors.
+* Go to the admin interface, and check if there are failed tasks. If so, the
+ tasks will contain an error message.
-.. _troubleshooting-convertpixelcache:
+Consumer fails to pickup any new files
+######################################
-Consumer dies with ``convert: unable to extent pixel cache``
-------------------------------------------------------------
+If you notice that the consumer will only pickup files in the consumption
+directory at startup, but won't find any other files added later, check out
+the configuration file and enable filesystem polling with the setting
+``PAPERLESS_CONSUMER_POLLING``.
-During the consumption process, Paperless invokes ImageMagick's ``convert``
-program to translate the source document into something that the OCR engine can
-understand and this can burn a Very Large amount of memory if the original
-document is rather long. Similarly, if your system doesn't have a lot of
-memory to begin with (ie. a Raspberry Pi), then this can happen for even
-medium-sized documents.
+Operation not permitted
+#######################
-The solution is to tell ImageMagick *not* to Use All The RAM, as is its
-default, and instead tell it to used a fixed amount. ``convert`` will then
-break up the job into hundreds of individual files and use them to slowly
-compile the finished image. Simply set ``PAPERLESS_CONVERT_MEMORY_LIMIT`` in
-``/etc/paperless.conf`` to something like ``32000000`` and you'll limit
-``convert`` to 32MB. Fiddle with this value as you like.
+You might see errors such as:
-**HOWEVER**: Simply setting this value may not be enough on system where
-``/tmp`` is mounted as tmpfs, as this is where ``convert`` will write its
-temporary files. In these cases (most Systemd machines), you need to tell
-ImageMagick to use a different space for its scratch work. You do this by
-setting ``PAPERLESS_CONVERT_TMPDIR`` in ``/etc/paperless.conf`` to somewhere
-that's actually on a physical disk (and writable by the user running
-Paperless), like ``/var/tmp/paperless`` or ``/home/my_user/tmp`` in a pinch.
+.. code::
+ chown: changing ownership of '../export': Operation not permitted
-.. _troubleshooting-decompressionbombwarning:
+The container tries to set file ownership on the listed directories. This is
+required so that the user running paperless inside docker has write permissions
+to these folders. This happens when pointing these directories to NFS shares,
+for example.
-DecompressionBombWarning and/or no text in the OCR output
----------------------------------------------------------
-Some users have had issues using Paperless to consume PDFs that were created
-by merging Very Large Scanned Images into one PDF. If this happens to you,
-it's likely because the PDF you've created contains some very large pages
-(millions of pixels) and the process of converting the PDF to a OCR-friendly
-image is exploding.
-
-Typically, this happens because the scanned images are created with a high
-DPI and then rolled into the PDF with an assumed DPI of 72 (the default).
-The best solution then is to specify the DPI used in the scan in the
-conversion-to-PDF step. So for example, if you scanned the original image
-with a DPI of 300, then merging the images into the single PDF with
-``convert`` should look like this:
-
-.. code:: bash
-
- $ convert -density 300 *.jpg finished.pdf
-
-For more information on this and situations like it, you should take a look
-at `Issue #118`_ as that's where this tip originated.
-
-.. _Issue #118: https://github.com/the-paperless-project/paperless/issues/118
\ No newline at end of file
+Ensure that `chown` is possible on these directories.
diff --git a/docs/usage_overview.rst b/docs/usage_overview.rst
new file mode 100644
index 000000000..7a4fd7740
--- /dev/null
+++ b/docs/usage_overview.rst
@@ -0,0 +1,403 @@
+**************
+Usage Overview
+**************
+
+Paperless is an application that manages your personal documents. With
+the help of a document scanner (see :ref:`scanners`), paperless transforms
+your wieldy physical document binders into a searchable archive and
+provides many utilities for finding and managing your documents.
+
+
+Terms and definitions
+#####################
+
+Paperless essentially consists of two different parts for managing your
+documents:
+
+* The *consumer* watches a specified folder and adds all documents in that
+ folder to paperless.
+* The *web server* provides a UI that you use to manage and search for your
+ scanned documents.
+
+Each document has a couple of fields that you can assign to them:
+
+* A *Document* is a piece of paper that sometimes contains valuable
+ information.
+* The *correspondent* of a document is the person, institution or company that
+ a document either originates form, or is sent to.
+* A *tag* is a label that you can assign to documents. Think of labels as more
+ powerful folders: Multiple documents can be grouped together with a single
+ tag, however, a single document can also have multiple tags. This is not
+ possible with folders. The reason folders are not implemented in paperless
+ is simply that tags are much more versatile than folders.
+* A *document type* is used to demarcate the type of a document such as letter,
+ bank statement, invoice, contract, etc. It is used to identify what a document
+ is about.
+* The *date added* of a document is the date the document was scanned into
+ paperless. You cannot and should not change this date.
+* The *date created* of a document is the date the document was initially issued.
+ This can be the date you bought a product, the date you signed a contract, or
+ the date a letter was sent to you.
+* The *archive serial number* (short: ASN) of a document is the identifier of
+ the document in your physical document binders. See
+ :ref:`usage-recommended_workflow` below.
+* The *content* of a document is the text that was OCR'ed from the document.
+ This text is fed into the search engine and is used for matching tags,
+ correspondents and document types.
+
+
+Frontend overview
+#################
+
+.. warning::
+
+ TBD. Add some fancy screenshots!
+
+Adding documents to paperless
+#############################
+
+Once you've got Paperless setup, you need to start feeding documents into it.
+When adding documents to paperless, it will perform the following operations on
+your documents:
+
+1. OCR the document, if it has no text. Digital documents usually have text,
+ and this step will be skipped for those documents.
+2. Paperless will create an archiveable PDF/A document from your document.
+ If this document is coming from your scanner, it will have embedded selectable text.
+3. Paperless performs automatic matching of tags, correspondents and types on the
+ document before storing it in the database.
+
+.. hint::
+
+ This process can be configured to fit your needs. If you don't want paperless
+ to create archived versions for digital documents, you can configure that by
+ configuring ``PAPERLESS_OCR_MODE=skip_noarchive``. Please read the
+ :ref:`relevant section in the documentation `.
+
+.. note::
+
+ No matter which options you choose, Paperless will always store the original
+ document that it found in the consumption directory or in the mail and
+ will never overwrite that document. Archived versions are stored alongside the
+ original versions.
+
+
+The consumption directory
+=========================
+
+The primary method of getting documents into your database is by putting them in
+the consumption directory. The consumer runs in an infinite
+loop looking for new additions to this directory and when it finds them, it goes
+about the process of parsing them with the OCR, indexing what it finds, and storing
+it in the media directory.
+
+Getting stuff into this directory is up to you. If you're running Paperless
+on your local computer, you might just want to drag and drop files there, but if
+you're running this on a server and want your scanner to automatically push
+files to this directory, you'll need to setup some sort of service to accept the
+files from the scanner. Typically, you're looking at an FTP server like
+`Proftpd`_ or a Windows folder share with `Samba`_.
+
+.. _Proftpd: http://www.proftpd.org/
+.. _Samba: http://www.samba.org/
+
+.. TODO: hyperref to configuration of the location of this magic folder.
+
+Dashboard upload
+================
+
+The dashboard has a file drop field to upload documents to paperless. Simply drag a file
+onto this field or select a file with the file dialog. Multiple files are supported.
+
+
+Mobile upload
+=============
+
+The mobile app over at ``_ allows Android users
+to share any documents with paperless. This can be combined with any of the mobile
+scanning apps out there, such as Office Lens.
+
+Furthermore, there is the `Paperless App `_ as well,
+which no only has document upload, but also document editing and browsing.
+
+.. _usage-email:
+
+IMAP (Email)
+============
+
+You can tell paperless-ng to consume documents from your email accounts.
+This is a very flexible and powerful feature, if you regularly received documents
+via mail that you need to archive. The mail consumer can be configured by using the
+admin interface in the following manner:
+
+1. Define e-mail accounts.
+2. Define mail rules for your account.
+
+These rules perform the following:
+
+1. Connect to the mail server.
+2. Fetch all matching mails (as defined by folder, maximum age and the filters)
+3. Check if there are any consumable attachments.
+4. If so, instruct paperless to consume the attachments and optionally
+ use the metadata provided in the rule for the new document.
+5. If documents were consumed from a mail, the rule action is performed
+ on that mail.
+
+Paperless will completely ignore mails that do not match your filters. It will also
+only perform the action on mails that it has consumed documents from.
+
+The actions all ensure that the same mail is not consumed twice by different means.
+These are as follows:
+
+* **Delete:** Immediately deletes mail that paperless has consumed documents from.
+ Use with caution.
+* **Mark as read:** Mark consumed mail as read. Paperless will not consume documents
+ from already read mails. If you read a mail before paperless sees it, it will be
+ ignored.
+* **Flag:** Sets the 'important' flag on mails with consumed documents. Paperless
+ will not consume flagged mails.
+* **Move to folder:** Moves consumed mails out of the way so that paperless wont
+ consume them again.
+
+.. caution::
+
+ The mail consumer will perform these actions on all mails it has consumed
+ documents from. Keep in mind that the actual consumption process may fail
+ for some reason, leaving you with missing documents in paperless.
+
+.. note::
+
+ With the correct set of rules, you can completely automate your email documents.
+ Create rules for every correspondent you receive digital documents from and
+ paperless will read them automatically. The default action "mark as read" is
+ pretty tame and will not cause any damage or data loss whatsoever.
+
+ You can also setup a special folder in your mail account for paperless and use
+ your favorite mail client to move to be consumed mails into that folder
+ automatically or manually and tell paperless to move them to yet another folder
+ after consumption. It's up to you.
+
+.. note::
+
+ Paperless will process the rules in the order defined in the admin page.
+
+ You can define catch-all rules and have them executed last to consume
+ any documents not matched by previous rules. Such a rule may assign an "Unknown
+ mail document" tag to consumed documents so you can inspect them further.
+
+Paperless is set up to check your mails every 10 minutes. This can be configured on the
+'Scheduled tasks' page in the admin.
+
+
+REST API
+========
+
+You can also submit a document using the REST API, see :ref:`api-file_uploads` for details.
+
+.. _basic-searching:
+
+
+Best practices
+##############
+
+Paperless offers a couple tools that help you organize your document collection. However,
+it is up to you to use them in a way that helps you organize documents and find specific
+documents when you need them. This section offers a couple ideas for managing your collection.
+
+Document types allow you to classify documents according to what they are. You can define
+types such as "Receipt", "Invoice", or "Contract". If you used to collect all your receipts
+in a single binder, you can recreate that system in paperless by defining a document type,
+assigning documents to that type and then filtering by that type to only see all receipts.
+
+Not all documents need document types. Sometimes its hard to determine what the type of a
+document is or it is hard to justify creating a document type that you only need once or twice.
+This is okay. As long as the types you define help you organize your collection in the way
+you want, paperless is doing its job.
+
+Tags can be used in many different ways. Think of tags are more versatile folders or binders.
+If you have a binder for documents related to university / your car or health care, you can
+create these binders in paperless by creating tags and assigning them to relevant documents.
+Just as with documents, you can filter the document list by tags and only see documents of
+a certain topic.
+
+With physical documents, you'll often need to decide which folder the document belongs to.
+The advantage of tags over folders and binders is that a single document can have multiple
+tags. A physical document cannot magically appear in two different folders, but with tags,
+this is entirely possible.
+
+.. hint::
+
+ This can be used in many different ways. One example: Imagine you're working on a particular
+ task, such as signing up for university. Usually you'll need to collect a bunch of different
+ documents that are already sorted into various folders. With the tag system of paperless,
+ you can create a new group of documents that are relevant to this task without destroying
+ the already existing organization. When you're done with the task, you could delete the
+ tag again, which would be equal to sorting documents back into the folder they belong into.
+ Or keep the tag, up to you.
+
+All of the logic above applies to correspondents as well. Attach them to documents if you
+feel that they help you organize your collection.
+
+When you've started organizing your documents, create a couple saved views for document collections
+you regularly access. This is equal to having labeled physical binders on your desk, except
+that these saved views are dynamic and simply update themselves as you add documents to the system.
+
+Here are a couple examples of tags and types that you could use in your collection.
+
+* An ``inbox`` tag for newly added documents that you haven't manually edited yet.
+* A tag ``car`` for everything car related (repairs, registration, insurance, etc)
+* A tag ``todo`` for documents that you still need to do something with, such as reply, or
+ perform some task online.
+* A tag ``bank account x`` for all bank statement related to that account.
+* A tag ``mail`` for anything that you added to paperless via its mail processing capabilities.
+* A tag ``missing_metadata`` when you still need to add some metadata to a document, but can't
+ or don't want to do this right now.
+
+Searching
+#########
+
+Paperless offers an extensive searching mechanism that is designed to allow you to quickly
+find a document you're looking for (for example, that thing that just broke and you bought
+a couple months ago, that contract you signed 8 years ago).
+
+When you search paperless for a document, it tries to match this query against your documents.
+Paperless will look for matching documents by inspecting their content, title, correspondent,
+type and tags. Paperless returns a scored list of results, so that documents matching your query
+better will appear further up in the search results.
+
+By default, paperless returns only documents which contain all words typed in the search bar.
+However, paperless also offers advanced search syntax if you want to drill down the results
+further.
+
+Matching documents with logical expressions:
+
+.. code::
+
+ shopname AND (product1 OR product2)
+
+Matching specific tags, correspondents or types:
+
+.. code::
+
+ type:invoice tag:unpaid
+ correspondent:university certificate
+
+Matching dates:
+
+.. code::
+
+ created:[2005 to 2009]
+ added:yesterday
+ modified:today
+
+Matching inexact words:
+
+.. code::
+
+ produ*name
+
+.. note::
+
+ Inexact terms are hard for search indexes. These queries might take a while to execute. That's why paperless offers
+ auto complete and query correction.
+
+All of these constructs can be combined as you see fit.
+If you want to learn more about the query language used by paperless, paperless uses Whoosh's default query language.
+Head over to `Whoosh query language `_.
+For details on what date parsing utilities are available, see
+`Date parsing `_.
+
+
+.. _usage-recommended_workflow:
+
+The recommended workflow
+########################
+
+Once you have familiarized yourself with paperless and are ready to use it
+for all your documents, the recommended workflow for managing your documents
+is as follows. This workflow also takes into account that some documents
+have to be kept in physical form, but still ensures that you get all the
+advantages for these documents as well.
+
+The following diagram shows how easy it is to manage your documents.
+
+.. image:: _static/recommended_workflow.png
+
+Preparations in paperless
+=========================
+
+* Create an inbox tag that gets assigned to all new documents.
+* Create a TODO tag.
+
+Processing of the physical documents
+====================================
+
+Keep a physical inbox. Whenever you receive a document that you need to
+archive, put it into your inbox. Regularly, do the following for all documents
+in your inbox:
+
+1. For each document, decide if you need to keep the document in physical
+ form. This applies to certain important documents, such as contracts and
+ certificates.
+2. If you need to keep the document, write a running number on the document
+ before scanning, starting at one and counting upwards. This is the archive
+ serial number, or ASN in short.
+3. Scan the document.
+4. If the document has an ASN assigned, store it in a *single* binder, sorted
+ by ASN. Don't order this binder in any other way.
+5. If the document has no ASN, throw it away. Yay!
+
+Over time, you will notice that your physical binder will fill up. If it is
+full, label the binder with the range of ASNs in this binder (i.e., "Documents
+1 to 343"), store the binder in your cellar or elsewhere, and start a new
+binder.
+
+The idea behind this process is that you will never have to use the physical
+binders to find a document. If you need a specific physical document, you
+may find this document by:
+
+1. Searching in paperless for the document.
+2. Identify the ASN of the document, since it appears on the scan.
+3. Grab the relevant document binder and get the document. This is easy since
+ they are sorted by ASN.
+
+Processing of documents in paperless
+====================================
+
+Once you have scanned in a document, proceed in paperless as follows.
+
+1. If the document has an ASN, assign the ASN to the document.
+2. Assign a correspondent to the document (i.e., your employer, bank, etc)
+ This isn't strictly necessary but helps in finding a document when you need
+ it.
+3. Assign a document type (i.e., invoice, bank statement, etc) to the document
+ This isn't strictly necessary but helps in finding a document when you need
+ it.
+4. Assign a proper title to the document (the name of an item you bought, the
+ subject of the letter, etc)
+5. Check that the date of the document is correct. Paperless tries to read
+ the date from the content of the document, but this fails sometimes if the
+ OCR is bad or multiple dates appear on the document.
+6. Remove inbox tags from the documents.
+
+.. hint::
+
+ You can setup manual matching rules for your correspondents and tags and
+ paperless will assign them automatically. After consuming a couple documents,
+ you can even ask paperless to *learn* when to assign tags and correspondents
+ by itself. For details on this feature, see :ref:`advanced-matching`.
+
+Task management
+===============
+
+Some documents require attention and require you to act on the document. You
+may take two different approaches to handle these documents based on how
+regularly you intent to use paperless and scan documents.
+
+* If you scan and process your documents in paperless regularly, assign a
+ TODO tag to all scanned documents that you need to process. Create a saved
+ view on the dashboard that shows all documents with this tag.
+* If you do not scan documents regularly and use paperless solely for archiving,
+ create a physical todo box next to your physical inbox and put documents you
+ need to process in the TODO box. When you performed the task associated with
+ the document, move it to the inbox.
diff --git a/docs/utilities.rst b/docs/utilities.rst
deleted file mode 100644
index 3c7e8d542..000000000
--- a/docs/utilities.rst
+++ /dev/null
@@ -1,284 +0,0 @@
-.. _utilities:
-
-Utilities
-=========
-
-There's basically three utilities to Paperless: the webserver, consumer, and
-if needed, the exporter. They're all detailed here.
-
-
-.. _utilities-webserver:
-
-The Webserver
--------------
-
-At the heart of it, Paperless is a simple Django webservice, and the entire
-interface is based on Django's standard admin interface. Once running, visiting
-the URL for your service delivers the admin, through which you can get a
-detailed listing of all available documents, search for specific files, and
-download whatever it is you're looking for.
-
-
-.. _utilities-webserver-howto:
-
-How to Use It
-.............
-
-The webserver is started via the ``manage.py`` script:
-
-.. code-block:: shell-session
-
- $ /path/to/paperless/src/manage.py runserver
-
-By default, the server runs on localhost, port 8000, but you can change this
-with a few arguments, run ``manage.py --help`` for more information.
-
-Add the option ``--noreload`` to reduce resource usage. Otherwise, the server
-continuously polls all source files for changes to auto-reload them.
-
-Note that when exiting this command your webserver will disappear.
-If you want to run this full-time (which is kind of the point)
-you'll need to have it start in the background -- something you'll need to
-figure out for your own system. To get you started though, there are Systemd
-service files in the ``scripts`` directory.
-
-
-.. _utilities-consumer:
-
-The Consumer
-------------
-
-The consumer script runs in an infinite loop, constantly looking at a directory
-for documents to parse and index. The process is pretty straightforward:
-
-1. Look in ``CONSUMPTION_DIR`` for a document. If one is found, go to #2.
- If not, wait 10 seconds and try again. On Linux, new documents are detected
- instantly via inotify, so there's no waiting involved.
-2. Parse the document with Tesseract
-3. Create a new record in the database with the OCR'd text
-4. Attempt to automatically assign document attributes by doing some guesswork.
- Read up on the :ref:`guesswork documentation` for more
- information about this process.
-5. Encrypt the document (if you have a passphrase set) and store it in the
- ``media`` directory under ``documents/originals``.
-6. Go to #1.
-
-
-.. _utilities-consumer-howto:
-
-How to Use It
-.............
-
-The consumer is started via the ``manage.py`` script:
-
-.. code-block:: shell-session
-
- $ /path/to/paperless/src/manage.py document_consumer
-
-This starts the service that will consume documents as they appear in
-``CONSUMPTION_DIR``.
-
-Note that this command runs continuously, so exiting it will mean your webserver
-disappears. If you want to run this full-time (which is kind of the point)
-you'll need to have it start in the background -- something you'll need to
-figure out for your own system. To get you started though, there are Systemd
-service files in the ``scripts`` directory.
-
-Some command line arguments are available to customize the behavior of the
-consumer. By default it will use ``/etc/paperless.conf`` values. Display the
-help with:
-
-.. code-block:: shell-session
-
- $ /path/to/paperless/src/manage.py document_consumer --help
-
-.. _utilities-exporter:
-
-The Exporter
-------------
-
-Tired of fiddling with Paperless, or just want to do something stupid and are
-afraid of accidentally damaging your files? You can export all of your
-documents into neatly named, dated, and unencrypted files.
-
-
-.. _utilities-exporter-howto:
-
-How to Use It
-.............
-
-This too is done via the ``manage.py`` script:
-
-.. code-block:: shell-session
-
- $ /path/to/paperless/src/manage.py document_exporter /path/to/somewhere/
-
-This will dump all of your unencrypted documents into ``/path/to/somewhere``
-for you to do with as you please. The files are accompanied with a special
-file, ``manifest.json`` which can be used to :ref:`import the files
-` at a later date if you wish.
-
-
-.. _utilities-exporter-howto-docker:
-
-Docker
-______
-
-If you are :ref:`using Docker `, running the
-expoorter is almost as easy. To mount a volume for exports, follow the
-instructions in the ``docker-compose.yml.example`` file for the ``/export``
-volume (making the changes in your own ``docker-compose.yml`` file, of course).
-Once you have the volume mounted, the command to run an export is:
-
-.. code-block:: shell-session
-
- $ docker-compose run --rm consumer document_exporter /export
-
-If you prefer to use ``docker run`` directly, supplying the necessary commandline
-options:
-
-.. code-block:: shell-session
-
- $ # Identify your containers
- $ docker-compose ps
- Name Command State Ports
- -------------------------------------------------------------------------
- paperless_consumer_1 /sbin/docker-entrypoint.sh ... Exit 0
- paperless_webserver_1 /sbin/docker-entrypoint.sh ... Exit 0
-
- $ # Make sure to replace your passphrase and remove or adapt the id mapping
- $ docker run --rm \
- --volumes-from paperless_data_1 \
- --volume /path/to/arbitrary/place:/export \
- -e PAPERLESS_PASSPHRASE=YOUR_PASSPHRASE \
- -e USERMAP_UID=1000 -e USERMAP_GID=1000 \
- paperless document_exporter /export
-
-
-.. _utilities-importer:
-
-The Importer
-------------
-
-Looking to transfer Paperless data from one instance to another, or just want
-to restore from a backup? This is your go-to toy.
-
-
-.. _utilities-importer-howto:
-
-How to Use It
-.............
-
-The importer works just like the exporter. You point it at a directory, and
-the script does the rest of the work:
-
-.. code-block:: shell-session
-
- $ /path/to/paperless/src/manage.py document_importer /path/to/somewhere/
-
-Docker
-______
-
-Assuming that you've already gone through the steps above in the
-:ref:`export ` section, then the easiest thing
-to do is just re-use the ``/export`` path you already setup:
-
-.. code-block:: shell-session
-
- $ docker-compose run --rm consumer document_importer /export
-
-Similarly, if you're not using docker-compose, you can adjust the export
-instructions above to do the import.
-
-
-.. _utilities-retagger:
-
-Re-running your tagging and correspondent matchers
---------------------------------------------------
-
-Say you've imported a few hundred documents and now want to introduce
-a tag or set up a new correspondent, and apply its matching to all of
-the currently-imported docs. This problem is common enough that
-there are tools for it.
-
-
-.. _utilities-retagger-howto:
-
-How to Do It
-............
-
-This too is done via the ``manage.py`` script:
-
-.. code:: bash
-
- $ /path/to/paperless/src/manage.py document_retagger
-
-Run this after changing or adding tagging rules. It'll loop over all
-of the documents in your database and attempt to match all of your
-tags to them. If one matches, it'll be applied. And don't worry, you
-can run this as often as you like, it won't double-tag a document.
-
-.. code:: bash
-
- $ /path/to/paperless/src/manage.py document_correspondents
-
-This is the similar command to run after adding or changing a correspondent.
-
-.. _utilities-encyption:
-
-Enabling Encrpytion
--------------------
-
-Let's say you've imported a few documents to play around with paperless and now
-you are using it more seriously and want to enable encryption of your files.
-
-.. utilities-encryption-howto:
-
-Basic Syntax
-.............
-
-Again we'll use the ``manage.py`` script, passing ``change_storage_type``:
-
-.. code:: console
-
- $ /path/to/paperless/src/manage.py change_storage_type --help
- usage: manage.py change_storage_type [-h] [--version] [-v {0,1,2,3}]
- [--settings SETTINGS]
- [--pythonpath PYTHONPATH] [--traceback]
- [--no-color] [--passphrase PASSPHRASE]
- {gpg,unencrypted} {gpg,unencrypted}
-
- This is how you migrate your stored documents from an encrypted state to an
- unencrypted one (or vice-versa)
-
- positional arguments:
- {gpg,unencrypted} The state you want to change your documents from
- {gpg,unencrypted} The state you want to change your documents to
-
- optional arguments:
- --passphrase PASSPHRASE
- If PAPERLESS_PASSPHRASE isn't set already, you need to
- specify it here
-
-Enabling Encryption
-...................
-
-Basic usage to enable encryption of your document store (**USE A MORE SECURE PASSPHRASE**):
-
-(Note: If ``PAPERLESS_PASSPHRASE`` isn't set already, you need to specify it here)
-
-.. code:: bash
-
- $ /path/to/paperless/src/manage.py change_storage_type [--passphrase SECR3TP4SSPHRA$E] unencrypted gpg
-
-
-Disabling Encryption
-....................
-
-Basic usage to enable encryption of your document store:
-
-(Note: Again, if ``PAPERLESS_PASSPHRASE`` isn't set already, you need to specify it here)
-
-.. code:: bash
-
- $ /path/to/paperless/src/manage.py change_storage_type [--passphrase SECR3TP4SSPHRA$E] gpg unencrypted
diff --git a/management/commands/create_superuser_with_password.py b/management/commands/create_superuser_with_password.py
deleted file mode 100644
index 830c1e167..000000000
--- a/management/commands/create_superuser_with_password.py
+++ /dev/null
@@ -1,38 +0,0 @@
-from django.contrib.auth.management.commands import createsuperuser
-from django.core.management import CommandError
-
-
-class Command(createsuperuser.Command):
- help = 'Crate a superuser, and allow password to be provided'
-
- def add_arguments(self, parser):
- super(Command, self).add_arguments(parser)
- parser.add_argument(
- '--password', dest='password', default=None,
- help='Specifies the password for the superuser.',
- )
- parser.add_argument(
- '--preserve', dest='preserve', default=False, action='store_true',
- help='Exit normally if the user already exists.',
- )
-
- def handle(self, *args, **options):
- password = options.get('password')
- username = options.get('username')
- database = options.get('database')
-
- if password and not username:
- raise CommandError("--username is required if specifying --password")
-
- if username and options.get('preserve'):
- exists = self.UserModel._default_manager.db_manager(database).filter(username=username).exists()
- if exists:
- self.stdout.write("User exists, exiting normally due to --preserve")
- return
-
- super(Command, self).handle(*args, **options)
-
- if password:
- user = self.UserModel._default_manager.db_manager(database).get(username=username)
- user.set_password(password)
- user.save()
diff --git a/overrides/README.md b/overrides/README.md
deleted file mode 100644
index f190c1407..000000000
--- a/overrides/README.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# Customizing Paperless
-
-*See customization
-[documentation](https://paperless.readthedocs.io/en/latest/customising.html)
-for more detail!*
-
-The example `.css` and `.js` snippets in this folder can be placed into
-one of two files in your ``PAPERLESS_MEDIADIR`` folder: `overrides.js` or
-`overrides.css`. Please feel free to submit pull requests to the main
-repository with other examples of customizations that you think others may
-find useful.
\ No newline at end of file
diff --git a/paperless.conf.example b/paperless.conf.example
index b99995b8f..910fc22a0 100644
--- a/paperless.conf.example
+++ b/paperless.conf.example
@@ -1,285 +1,62 @@
-# Sample paperless.conf
-# Copy this file to /etc/paperless.conf and modify it to suit your needs.
-# As this file contains passwords it should only be readable by the user
-# running paperless.
+# Have a look at the docs for documentation.
+# https://paperless-ng.readthedocs.io/en/latest/configuration.html
+# Debug. Only enable this for development.
-###############################################################################
-#### Paths & Folders ####
-###############################################################################
+#PAPERLESS_DEBUG=false
-# This where your documents should go to be consumed. Make sure that it exists
-# and that the user running the paperless service can read/write its contents
-# before you start Paperless.
-PAPERLESS_CONSUMPTION_DIR=""
+# Required services
+#PAPERLESS_REDIS=redis://localhost:6379
+#PAPERLESS_DBHOST=localhost
+#PAPERLESS_DBPORT=5432
+#PAPERLESS_DBNAME=paperless
+#PAPERLESS_DBUSER=paperless
+#PAPERLESS_DBPASS=paperless
-# You can specify where you want the SQLite database to be stored instead of
-# the default location of /data/ within the install directory.
-#PAPERLESS_DBDIR=/path/to/database/file
+# Paths and folders
+#PAPERLESS_CONSUMPTION_DIR=../consume
+#PAPERLESS_DATA_DIR=../data
+#PAPERLESS_MEDIA_ROOT=../media
+#PAPERLESS_STATICDIR=../static
+#PAPERLESS_FILENAME_FORMAT=
-# Override the default MEDIA_ROOT here. This is where all files are stored.
-# The default location is /media/documents/ within the install folder.
-#PAPERLESS_MEDIADIR=/path/to/media
+# Security and hosting
+#PAPERLESS_SECRET_KEY=change-me
+#PAPERLESS_ALLOWED_HOSTS=example.com,www.example.com
+#PAPERLESS_CORS_ALLOWED_HOSTS=localhost:8080,example.com,localhost:8000
+#PAPERLESS_FORCE_SCRIPT_NAME=
+#PAPERLESS_STATIC_URL=/static/
+#PAPERLESS_AUTO_LOGIN_USERNAME=
+#PAPERLESS_COOKIE_PREFIX=
-# Override the default STATIC_ROOT here. This is where all static files
-# created using "collectstatic" manager command are stored.
-#PAPERLESS_STATICDIR=""
+# OCR settings
-
-# Override the MEDIA_URL here. Unless you're hosting Paperless off a subdomain
-# like /paperless/, you probably don't need to change this.
-#PAPERLESS_MEDIA_URL="/media/"
-
-# Override the STATIC_URL here. Unless you're hosting Paperless off a
-# subdomain like /paperless/, you probably don't need to change this.
-#PAPERLESS_STATIC_URL="/static/"
-
-
-# These values are required if you want paperless to check a particular email
-# box every 10 minutes and attempt to consume documents from there. If you
-# don't define a HOST, mail checking will just be disabled.
-PAPERLESS_CONSUME_MAIL_HOST=""
-PAPERLESS_CONSUME_MAIL_PORT=""
-PAPERLESS_CONSUME_MAIL_USER=""
-PAPERLESS_CONSUME_MAIL_PASS=""
-
-# Override the default IMAP inbox here. If not set Paperless defaults to
-# "INBOX".
-#PAPERLESS_CONSUME_MAIL_INBOX="INBOX"
-
-# Any email sent to the target account that does not contain this text will be
-# ignored.
-PAPERLESS_EMAIL_SECRET=""
-
-# Specify a filename format for the document (directories are supported)
-# Use the following placeholders:
-# * {correspondent}
-# * {title}
-# * {created}
-# * {added}
-# * {tags[KEY]} If your tags conform to key_value or key-value
-# * {tags[INDEX]} If your tags are strings, select the tag by index
-# Uniqueness of filenames is ensured, as an incrementing counter is attached
-# to each filename.
-#PAPERLESS_FILENAME_FORMAT=""
-
-###############################################################################
-#### Security ####
-###############################################################################
-
-# Controls whether django's debug mode is enabled. Disable this on production
-# systems. Debug mode is enabled by default.
-#PAPERLESS_DEBUG="true"
-
-
-# Paperless can be instructed to attempt to encrypt your PDF files with GPG
-# using the PAPERLESS_PASSPHRASE specified below. If however you're not
-# concerned about encrypting these files (for example if you have disk
-# encryption locally) then you don't need this and can safely leave this value
-# un-set.
-#
-# One final note about the passphrase. Once you've consumed a document with
-# one passphrase, DON'T CHANGE IT. Paperless assumes this to be a constant and
-# can't properly export documents that were encrypted with an old passphrase if
-# you've since changed it to a new one.
-#
-# The default is to not use encryption at all.
-#PAPERLESS_PASSPHRASE="secret"
-
-
-# The secret key has a default that should be fine so long as you're hosting
-# Paperless on a closed network. However, if you're putting this anywhere
-# public, you should change the key to something unique and verbose.
-#PAPERLESS_SECRET_KEY="change-me"
-
-
-# If you're planning on putting Paperless on the open internet, then you
-# really should set this value to the domain name you're using. Failing to do
-# so leaves you open to HTTP host header attacks:
-# https://docs.djangoproject.com/en/1.10/topics/security/#host-headers-virtual-hosting
-#
-# Just remember that this is a comma-separated list, so "example.com" is fine,
-# as is "example.com,www.example.com", but NOT " example.com" or "example.com,"
-#PAPERLESS_ALLOWED_HOSTS="example.com,www.example.com"
-
-# If you decide to use the Paperless API in an ajax call, you need to add your
-# servers to the list of allowed hosts that can do CORS calls. By default
-# Paperless allows calls from localhost:8080, but you'd like to change that,
-# you can set this value to a comma-separated list.
-#PAPERLESS_CORS_ALLOWED_HOSTS="localhost:8080,example.com,localhost:8000"
-
-# To host paperless under a subpath url like example.com/paperless you set
-# this value to /paperless. No trailing slash!
-#
-# https://docs.djangoproject.com/en/1.11/ref/settings/#force-script-name
-#PAPERLESS_FORCE_SCRIPT_NAME=""
-
-# If you are using alternative authentication means or are just using paperless
-# as a single user on a small private network, this option allows you to disable
-# user authentication if you set it to "true"
-#PAPERLESS_DISABLE_LOGIN="false"
-
-###############################################################################
-#### Software Tweaks ####
-###############################################################################
-
-# After a document is consumed, Paperless can trigger an arbitrary script if
-# you like. This script will be passed a number of arguments for you to work
-# with. The default is blank, which means nothing will be executed. For more
-# information, take a look at the docs:
-# http://paperless.readthedocs.org/en/latest/consumption.html#hooking-into-the-consumption-process
-#PAPERLESS_POST_CONSUME_SCRIPT="/path/to/an/arbitrary/script.sh"
-
-# By default, when clicking on a document within the web interface, the
-# browser will prompt the user to save the document to disk. By setting this to
-# "true", the document will instead be opened in the browser, if possible.
-#PAPERLESS_INLINE_DOC="false"
-
-# By default, paperless will check the document text for document date information.
-# Uncomment the line below to enable checking the document filename for date
-# information. The date order can be set to any option as specified in
-# https://dateparser.readthedocs.io/en/latest/#settings. The filename will be
-# checked first, and if nothing is found, the document text will be checked
-# as normal.
-#PAPERLESS_FILENAME_DATE_ORDER="YMD"
-
-# Sometimes devices won't create filenames which can be parsed properly
-# by the filename parser (see
-# https://paperless.readthedocs.io/en/latest/guesswork.html).
-#
-# This setting allows to specify a list of transformations
-# in regular expression syntax, which are passed in order to re.sub.
-# Transformation stops after the first match, so at most one transformation
-# is applied.
-#
-# Syntax is a JSON array of dictionaries containing "pattern" and "repl"
-# as keys.
-#
-# The example below transforms filenames created by a Brother ADS-2400N
-# document scanner in its standard configuration `Name_Date_Count', so that
-# count is used as title, name as tag and date can be parsed by paperless.
-#PAPERLESS_FILENAME_PARSE_TRANSFORMS=[{"pattern":"^([a-z]+)_(\\d{8})_(\\d{6})_([0-9]+)\\.", "repl":"\\2\\3Z - \\4 - \\1."}]
-
-#
-# The following values use sensible defaults for modern systems, but if you're
-# running Paperless on a low-resource device (like a Raspberry Pi), modifying
-# some of these values may be necessary.
-#
-
-
-# By default, Paperless will attempt to use all available CPU cores to process
-# a document, but if you would like to limit that, you can set this value to
-# an integer:
-#PAPERLESS_OCR_THREADS=1
-
-
-# Customize the default language that tesseract will attempt to use when
-# parsing documents. It should be a 3-letter language code consistent with ISO
-# 639: https://www.loc.gov/standards/iso639-2/php/code_list.php
#PAPERLESS_OCR_LANGUAGE=eng
-
-
-# On smaller systems, or even in the case of Very Large Documents, the consumer
-# may explode, complaining about how it's "unable to extend pixel cache". In
-# such cases, try setting this to a reasonably low value, like 32000000. The
-# default is to use whatever is necessary to do everything without writing to
-# disk, and units are in megabytes.
-#
-# For more information on how to use this value, you should probably search
-# the web for "MAGICK_MEMORY_LIMIT".
+#PAPERLESS_OCR_MODE=skip
+#PAPERLESS_OCR_OUTPUT_TYPE=pdfa
+#PAPERLESS_OCR_PAGES=1
+#PAPERLESS_OCR_IMAGE_DPI=300
+#PAPERLESS_OCR_USER_ARG={}
#PAPERLESS_CONVERT_MEMORY_LIMIT=0
-
-
-# Similar to the memory limit, if you've got a small system and your OS mounts
-# /tmp as tmpfs, you should set this to a path that's on a physical disk, like
-# /home/your_user/tmp or something. ImageMagick will use this as scratch space
-# when crunching through very large documents.
-#
-# For more information on how to use this value, you should probably search
-# the web for "MAGICK_TMPDIR".
#PAPERLESS_CONVERT_TMPDIR=/var/tmp/paperless
+# Software tweaks
-# By default the conversion density setting for documents is 300DPI, in some
-# cases it has proven useful to configure a lesser value.
-# This setting has a high impact on the physical size of tmp page files,
-# the speed of document conversion, and can affect the accuracy of OCR
-# results. Individual results can vary and this setting should be tested
-# thoroughly against the documents you are importing to see if it has any
-# impacts either negative or positive.
-# Testing on limited document sets has shown a setting of 200 can cut the
-# size of tmp files by 1/3, and speed up conversion by up to 4x
-# with little impact to OCR accuracy.
-#PAPERLESS_CONVERT_DENSITY=300
-
-
-# (This setting is ignored on Linux where inotify is used instead of a
-# polling loop.)
-# The number of seconds that Paperless will wait between checking
-# PAPERLESS_CONSUMPTION_DIR. If you tend to write documents to this directory
-# rarely, you may want to use a higher value than the default (10).
-#PAPERLESS_CONSUMER_LOOP_TIME=10
-
-
-# By default Paperless stops consuming a document if no language can be
-# detected. Set to true to consume documents even if the language detection
-# fails.
-#PAPERLESS_FORGIVING_OCR="false"
-
-
-# By default Paperless does not OCR a document if the text can be retrieved from
-# the document directly. Set to true to always OCR documents.
-#PAPERLESS_OCR_ALWAYS="false"
-
-
-###############################################################################
-#### Interface ####
-###############################################################################
-
-# Override the default UTC time zone here.
-# See https://docs.djangoproject.com/en/1.10/ref/settings/#std:setting-TIME_ZONE
-# for details on how to set it.
+#PAPERLESS_TASK_WORKERS=1
+#PAPERLESS_THREADS_PER_WORKER=1
#PAPERLESS_TIME_ZONE=UTC
+#PAPERLESS_CONSUMER_POLLING=10
+#PAPERLESS_CONSUMER_DELETE_DUPLICATES=false
+#PAPERLESS_OPTIMIZE_THUMBNAILS=true
+#PAPERLESS_POST_CONSUME_SCRIPT=/path/to/an/arbitrary/script.sh
+#PAPERLESS_FILENAME_DATE_ORDER=YMD
+#PAPERLESS_FILENAME_PARSE_TRANSFORMS=[]
+# Binaries
-# If set, Paperless will show document filters per financial year.
-# The dates must be in the format "mm-dd", for example "07-15" for July 15.
-#PAPERLESS_FINANCIAL_YEAR_START="mm-dd"
-#PAPERLESS_FINANCIAL_YEAR_END="mm-dd"
-
-
-# The number of items on each page in the web UI. This value must be a
-# positive integer, but if you don't define one in paperless.conf, a default of
-# 100 will be used.
-#PAPERLESS_LIST_PER_PAGE=100
-
-
-# The number of years for which a correspondent will be included in the recent
-# correspondents filter.
-#PAPERLESS_RECENT_CORRESPONDENT_YEARS=1
-
-###############################################################################
-#### Third-Party Binaries ####
-###############################################################################
-
-# There are a few external software packages that Paperless expects to find on
-# your system when it starts up. Unless you've done something creative with
-# their installation, you probably won't need to edit any of these. However,
-# if you've installed these programs somewhere where simply typing the name of
-# the program doesn't automatically execute it (ie. the program isn't in your
-# $PATH), then you'll need to specify the literal path for that program here.
-
-# Convert (part of the ImageMagick suite)
#PAPERLESS_CONVERT_BINARY=/usr/bin/convert
-
-# Ghostscript
-#PAPERLESS_GS_BINARY = /usr/bin/gs
-
-# Unpaper
-#PAPERLESS_UNPAPER_BINARY=/usr/bin/unpaper
-
-# Optipng (for optimising thumbnail sizes)
+#PAPERLESS_GS_BINARY=/usr/bin/gs
#PAPERLESS_OPTIPNG_BINARY=/usr/bin/optipng
diff --git a/presentation/README.md b/presentation/README.md
deleted file mode 100644
index 01820980e..000000000
--- a/presentation/README.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# Presentation
-
-This presentation was written with
-[reaveal.js](http://lab.hakim.se/reveal-js/), and requires no special
-software to view. Simply open `index.html` in a browser and you're good
-to go.
diff --git a/presentation/contrib/font-awesome-4.3.0/css/font-awesome.css b/presentation/contrib/font-awesome-4.3.0/css/font-awesome.css
deleted file mode 100644
index 2dcdc2207..000000000
--- a/presentation/contrib/font-awesome-4.3.0/css/font-awesome.css
+++ /dev/null
@@ -1,1801 +0,0 @@
-/*!
- * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome
- * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
- */
-/* FONT PATH
- * -------------------------- */
-@font-face {
- font-family: 'FontAwesome';
- src: url('../fonts/fontawesome-webfont.eot?v=4.3.0');
- src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.3.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.3.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.3.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.3.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.3.0#fontawesomeregular') format('svg');
- font-weight: normal;
- font-style: normal;
-}
-.fa {
- display: inline-block;
- font: normal normal normal 14px/1 FontAwesome;
- font-size: inherit;
- text-rendering: auto;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- transform: translate(0, 0);
-}
-/* makes the font 33% larger relative to the icon container */
-.fa-lg {
- font-size: 1.33333333em;
- line-height: 0.75em;
- vertical-align: -15%;
-}
-.fa-2x {
- font-size: 2em;
-}
-.fa-3x {
- font-size: 3em;
-}
-.fa-4x {
- font-size: 4em;
-}
-.fa-5x {
- font-size: 5em;
-}
-.fa-fw {
- width: 1.28571429em;
- text-align: center;
-}
-.fa-ul {
- padding-left: 0;
- margin-left: 2.14285714em;
- list-style-type: none;
-}
-.fa-ul > li {
- position: relative;
-}
-.fa-li {
- position: absolute;
- left: -2.14285714em;
- width: 2.14285714em;
- top: 0.14285714em;
- text-align: center;
-}
-.fa-li.fa-lg {
- left: -1.85714286em;
-}
-.fa-border {
- padding: .2em .25em .15em;
- border: solid 0.08em #eeeeee;
- border-radius: .1em;
-}
-.pull-right {
- float: right;
-}
-.pull-left {
- float: left;
-}
-.fa.pull-left {
- margin-right: .3em;
-}
-.fa.pull-right {
- margin-left: .3em;
-}
-.fa-spin {
- -webkit-animation: fa-spin 2s infinite linear;
- animation: fa-spin 2s infinite linear;
-}
-.fa-pulse {
- -webkit-animation: fa-spin 1s infinite steps(8);
- animation: fa-spin 1s infinite steps(8);
-}
-@-webkit-keyframes fa-spin {
- 0% {
- -webkit-transform: rotate(0deg);
- transform: rotate(0deg);
- }
- 100% {
- -webkit-transform: rotate(359deg);
- transform: rotate(359deg);
- }
-}
-@keyframes fa-spin {
- 0% {
- -webkit-transform: rotate(0deg);
- transform: rotate(0deg);
- }
- 100% {
- -webkit-transform: rotate(359deg);
- transform: rotate(359deg);
- }
-}
-.fa-rotate-90 {
- filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
- -webkit-transform: rotate(90deg);
- -ms-transform: rotate(90deg);
- transform: rotate(90deg);
-}
-.fa-rotate-180 {
- filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
- -webkit-transform: rotate(180deg);
- -ms-transform: rotate(180deg);
- transform: rotate(180deg);
-}
-.fa-rotate-270 {
- filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
- -webkit-transform: rotate(270deg);
- -ms-transform: rotate(270deg);
- transform: rotate(270deg);
-}
-.fa-flip-horizontal {
- filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);
- -webkit-transform: scale(-1, 1);
- -ms-transform: scale(-1, 1);
- transform: scale(-1, 1);
-}
-.fa-flip-vertical {
- filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);
- -webkit-transform: scale(1, -1);
- -ms-transform: scale(1, -1);
- transform: scale(1, -1);
-}
-:root .fa-rotate-90,
-:root .fa-rotate-180,
-:root .fa-rotate-270,
-:root .fa-flip-horizontal,
-:root .fa-flip-vertical {
- filter: none;
-}
-.fa-stack {
- position: relative;
- display: inline-block;
- width: 2em;
- height: 2em;
- line-height: 2em;
- vertical-align: middle;
-}
-.fa-stack-1x,
-.fa-stack-2x {
- position: absolute;
- left: 0;
- width: 100%;
- text-align: center;
-}
-.fa-stack-1x {
- line-height: inherit;
-}
-.fa-stack-2x {
- font-size: 2em;
-}
-.fa-inverse {
- color: #ffffff;
-}
-/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
- readers do not read off random characters that represent icons */
-.fa-glass:before {
- content: "\f000";
-}
-.fa-music:before {
- content: "\f001";
-}
-.fa-search:before {
- content: "\f002";
-}
-.fa-envelope-o:before {
- content: "\f003";
-}
-.fa-heart:before {
- content: "\f004";
-}
-.fa-star:before {
- content: "\f005";
-}
-.fa-star-o:before {
- content: "\f006";
-}
-.fa-user:before {
- content: "\f007";
-}
-.fa-film:before {
- content: "\f008";
-}
-.fa-th-large:before {
- content: "\f009";
-}
-.fa-th:before {
- content: "\f00a";
-}
-.fa-th-list:before {
- content: "\f00b";
-}
-.fa-check:before {
- content: "\f00c";
-}
-.fa-remove:before,
-.fa-close:before,
-.fa-times:before {
- content: "\f00d";
-}
-.fa-search-plus:before {
- content: "\f00e";
-}
-.fa-search-minus:before {
- content: "\f010";
-}
-.fa-power-off:before {
- content: "\f011";
-}
-.fa-signal:before {
- content: "\f012";
-}
-.fa-gear:before,
-.fa-cog:before {
- content: "\f013";
-}
-.fa-trash-o:before {
- content: "\f014";
-}
-.fa-home:before {
- content: "\f015";
-}
-.fa-file-o:before {
- content: "\f016";
-}
-.fa-clock-o:before {
- content: "\f017";
-}
-.fa-road:before {
- content: "\f018";
-}
-.fa-download:before {
- content: "\f019";
-}
-.fa-arrow-circle-o-down:before {
- content: "\f01a";
-}
-.fa-arrow-circle-o-up:before {
- content: "\f01b";
-}
-.fa-inbox:before {
- content: "\f01c";
-}
-.fa-play-circle-o:before {
- content: "\f01d";
-}
-.fa-rotate-right:before,
-.fa-repeat:before {
- content: "\f01e";
-}
-.fa-refresh:before {
- content: "\f021";
-}
-.fa-list-alt:before {
- content: "\f022";
-}
-.fa-lock:before {
- content: "\f023";
-}
-.fa-flag:before {
- content: "\f024";
-}
-.fa-headphones:before {
- content: "\f025";
-}
-.fa-volume-off:before {
- content: "\f026";
-}
-.fa-volume-down:before {
- content: "\f027";
-}
-.fa-volume-up:before {
- content: "\f028";
-}
-.fa-qrcode:before {
- content: "\f029";
-}
-.fa-barcode:before {
- content: "\f02a";
-}
-.fa-tag:before {
- content: "\f02b";
-}
-.fa-tags:before {
- content: "\f02c";
-}
-.fa-book:before {
- content: "\f02d";
-}
-.fa-bookmark:before {
- content: "\f02e";
-}
-.fa-print:before {
- content: "\f02f";
-}
-.fa-camera:before {
- content: "\f030";
-}
-.fa-font:before {
- content: "\f031";
-}
-.fa-bold:before {
- content: "\f032";
-}
-.fa-italic:before {
- content: "\f033";
-}
-.fa-text-height:before {
- content: "\f034";
-}
-.fa-text-width:before {
- content: "\f035";
-}
-.fa-align-left:before {
- content: "\f036";
-}
-.fa-align-center:before {
- content: "\f037";
-}
-.fa-align-right:before {
- content: "\f038";
-}
-.fa-align-justify:before {
- content: "\f039";
-}
-.fa-list:before {
- content: "\f03a";
-}
-.fa-dedent:before,
-.fa-outdent:before {
- content: "\f03b";
-}
-.fa-indent:before {
- content: "\f03c";
-}
-.fa-video-camera:before {
- content: "\f03d";
-}
-.fa-photo:before,
-.fa-image:before,
-.fa-picture-o:before {
- content: "\f03e";
-}
-.fa-pencil:before {
- content: "\f040";
-}
-.fa-map-marker:before {
- content: "\f041";
-}
-.fa-adjust:before {
- content: "\f042";
-}
-.fa-tint:before {
- content: "\f043";
-}
-.fa-edit:before,
-.fa-pencil-square-o:before {
- content: "\f044";
-}
-.fa-share-square-o:before {
- content: "\f045";
-}
-.fa-check-square-o:before {
- content: "\f046";
-}
-.fa-arrows:before {
- content: "\f047";
-}
-.fa-step-backward:before {
- content: "\f048";
-}
-.fa-fast-backward:before {
- content: "\f049";
-}
-.fa-backward:before {
- content: "\f04a";
-}
-.fa-play:before {
- content: "\f04b";
-}
-.fa-pause:before {
- content: "\f04c";
-}
-.fa-stop:before {
- content: "\f04d";
-}
-.fa-forward:before {
- content: "\f04e";
-}
-.fa-fast-forward:before {
- content: "\f050";
-}
-.fa-step-forward:before {
- content: "\f051";
-}
-.fa-eject:before {
- content: "\f052";
-}
-.fa-chevron-left:before {
- content: "\f053";
-}
-.fa-chevron-right:before {
- content: "\f054";
-}
-.fa-plus-circle:before {
- content: "\f055";
-}
-.fa-minus-circle:before {
- content: "\f056";
-}
-.fa-times-circle:before {
- content: "\f057";
-}
-.fa-check-circle:before {
- content: "\f058";
-}
-.fa-question-circle:before {
- content: "\f059";
-}
-.fa-info-circle:before {
- content: "\f05a";
-}
-.fa-crosshairs:before {
- content: "\f05b";
-}
-.fa-times-circle-o:before {
- content: "\f05c";
-}
-.fa-check-circle-o:before {
- content: "\f05d";
-}
-.fa-ban:before {
- content: "\f05e";
-}
-.fa-arrow-left:before {
- content: "\f060";
-}
-.fa-arrow-right:before {
- content: "\f061";
-}
-.fa-arrow-up:before {
- content: "\f062";
-}
-.fa-arrow-down:before {
- content: "\f063";
-}
-.fa-mail-forward:before,
-.fa-share:before {
- content: "\f064";
-}
-.fa-expand:before {
- content: "\f065";
-}
-.fa-compress:before {
- content: "\f066";
-}
-.fa-plus:before {
- content: "\f067";
-}
-.fa-minus:before {
- content: "\f068";
-}
-.fa-asterisk:before {
- content: "\f069";
-}
-.fa-exclamation-circle:before {
- content: "\f06a";
-}
-.fa-gift:before {
- content: "\f06b";
-}
-.fa-leaf:before {
- content: "\f06c";
-}
-.fa-fire:before {
- content: "\f06d";
-}
-.fa-eye:before {
- content: "\f06e";
-}
-.fa-eye-slash:before {
- content: "\f070";
-}
-.fa-warning:before,
-.fa-exclamation-triangle:before {
- content: "\f071";
-}
-.fa-plane:before {
- content: "\f072";
-}
-.fa-calendar:before {
- content: "\f073";
-}
-.fa-random:before {
- content: "\f074";
-}
-.fa-comment:before {
- content: "\f075";
-}
-.fa-magnet:before {
- content: "\f076";
-}
-.fa-chevron-up:before {
- content: "\f077";
-}
-.fa-chevron-down:before {
- content: "\f078";
-}
-.fa-retweet:before {
- content: "\f079";
-}
-.fa-shopping-cart:before {
- content: "\f07a";
-}
-.fa-folder:before {
- content: "\f07b";
-}
-.fa-folder-open:before {
- content: "\f07c";
-}
-.fa-arrows-v:before {
- content: "\f07d";
-}
-.fa-arrows-h:before {
- content: "\f07e";
-}
-.fa-bar-chart-o:before,
-.fa-bar-chart:before {
- content: "\f080";
-}
-.fa-twitter-square:before {
- content: "\f081";
-}
-.fa-facebook-square:before {
- content: "\f082";
-}
-.fa-camera-retro:before {
- content: "\f083";
-}
-.fa-key:before {
- content: "\f084";
-}
-.fa-gears:before,
-.fa-cogs:before {
- content: "\f085";
-}
-.fa-comments:before {
- content: "\f086";
-}
-.fa-thumbs-o-up:before {
- content: "\f087";
-}
-.fa-thumbs-o-down:before {
- content: "\f088";
-}
-.fa-star-half:before {
- content: "\f089";
-}
-.fa-heart-o:before {
- content: "\f08a";
-}
-.fa-sign-out:before {
- content: "\f08b";
-}
-.fa-linkedin-square:before {
- content: "\f08c";
-}
-.fa-thumb-tack:before {
- content: "\f08d";
-}
-.fa-external-link:before {
- content: "\f08e";
-}
-.fa-sign-in:before {
- content: "\f090";
-}
-.fa-trophy:before {
- content: "\f091";
-}
-.fa-github-square:before {
- content: "\f092";
-}
-.fa-upload:before {
- content: "\f093";
-}
-.fa-lemon-o:before {
- content: "\f094";
-}
-.fa-phone:before {
- content: "\f095";
-}
-.fa-square-o:before {
- content: "\f096";
-}
-.fa-bookmark-o:before {
- content: "\f097";
-}
-.fa-phone-square:before {
- content: "\f098";
-}
-.fa-twitter:before {
- content: "\f099";
-}
-.fa-facebook-f:before,
-.fa-facebook:before {
- content: "\f09a";
-}
-.fa-github:before {
- content: "\f09b";
-}
-.fa-unlock:before {
- content: "\f09c";
-}
-.fa-credit-card:before {
- content: "\f09d";
-}
-.fa-rss:before {
- content: "\f09e";
-}
-.fa-hdd-o:before {
- content: "\f0a0";
-}
-.fa-bullhorn:before {
- content: "\f0a1";
-}
-.fa-bell:before {
- content: "\f0f3";
-}
-.fa-certificate:before {
- content: "\f0a3";
-}
-.fa-hand-o-right:before {
- content: "\f0a4";
-}
-.fa-hand-o-left:before {
- content: "\f0a5";
-}
-.fa-hand-o-up:before {
- content: "\f0a6";
-}
-.fa-hand-o-down:before {
- content: "\f0a7";
-}
-.fa-arrow-circle-left:before {
- content: "\f0a8";
-}
-.fa-arrow-circle-right:before {
- content: "\f0a9";
-}
-.fa-arrow-circle-up:before {
- content: "\f0aa";
-}
-.fa-arrow-circle-down:before {
- content: "\f0ab";
-}
-.fa-globe:before {
- content: "\f0ac";
-}
-.fa-wrench:before {
- content: "\f0ad";
-}
-.fa-tasks:before {
- content: "\f0ae";
-}
-.fa-filter:before {
- content: "\f0b0";
-}
-.fa-briefcase:before {
- content: "\f0b1";
-}
-.fa-arrows-alt:before {
- content: "\f0b2";
-}
-.fa-group:before,
-.fa-users:before {
- content: "\f0c0";
-}
-.fa-chain:before,
-.fa-link:before {
- content: "\f0c1";
-}
-.fa-cloud:before {
- content: "\f0c2";
-}
-.fa-flask:before {
- content: "\f0c3";
-}
-.fa-cut:before,
-.fa-scissors:before {
- content: "\f0c4";
-}
-.fa-copy:before,
-.fa-files-o:before {
- content: "\f0c5";
-}
-.fa-paperclip:before {
- content: "\f0c6";
-}
-.fa-save:before,
-.fa-floppy-o:before {
- content: "\f0c7";
-}
-.fa-square:before {
- content: "\f0c8";
-}
-.fa-navicon:before,
-.fa-reorder:before,
-.fa-bars:before {
- content: "\f0c9";
-}
-.fa-list-ul:before {
- content: "\f0ca";
-}
-.fa-list-ol:before {
- content: "\f0cb";
-}
-.fa-strikethrough:before {
- content: "\f0cc";
-}
-.fa-underline:before {
- content: "\f0cd";
-}
-.fa-table:before {
- content: "\f0ce";
-}
-.fa-magic:before {
- content: "\f0d0";
-}
-.fa-truck:before {
- content: "\f0d1";
-}
-.fa-pinterest:before {
- content: "\f0d2";
-}
-.fa-pinterest-square:before {
- content: "\f0d3";
-}
-.fa-google-plus-square:before {
- content: "\f0d4";
-}
-.fa-google-plus:before {
- content: "\f0d5";
-}
-.fa-money:before {
- content: "\f0d6";
-}
-.fa-caret-down:before {
- content: "\f0d7";
-}
-.fa-caret-up:before {
- content: "\f0d8";
-}
-.fa-caret-left:before {
- content: "\f0d9";
-}
-.fa-caret-right:before {
- content: "\f0da";
-}
-.fa-columns:before {
- content: "\f0db";
-}
-.fa-unsorted:before,
-.fa-sort:before {
- content: "\f0dc";
-}
-.fa-sort-down:before,
-.fa-sort-desc:before {
- content: "\f0dd";
-}
-.fa-sort-up:before,
-.fa-sort-asc:before {
- content: "\f0de";
-}
-.fa-envelope:before {
- content: "\f0e0";
-}
-.fa-linkedin:before {
- content: "\f0e1";
-}
-.fa-rotate-left:before,
-.fa-undo:before {
- content: "\f0e2";
-}
-.fa-legal:before,
-.fa-gavel:before {
- content: "\f0e3";
-}
-.fa-dashboard:before,
-.fa-tachometer:before {
- content: "\f0e4";
-}
-.fa-comment-o:before {
- content: "\f0e5";
-}
-.fa-comments-o:before {
- content: "\f0e6";
-}
-.fa-flash:before,
-.fa-bolt:before {
- content: "\f0e7";
-}
-.fa-sitemap:before {
- content: "\f0e8";
-}
-.fa-umbrella:before {
- content: "\f0e9";
-}
-.fa-paste:before,
-.fa-clipboard:before {
- content: "\f0ea";
-}
-.fa-lightbulb-o:before {
- content: "\f0eb";
-}
-.fa-exchange:before {
- content: "\f0ec";
-}
-.fa-cloud-download:before {
- content: "\f0ed";
-}
-.fa-cloud-upload:before {
- content: "\f0ee";
-}
-.fa-user-md:before {
- content: "\f0f0";
-}
-.fa-stethoscope:before {
- content: "\f0f1";
-}
-.fa-suitcase:before {
- content: "\f0f2";
-}
-.fa-bell-o:before {
- content: "\f0a2";
-}
-.fa-coffee:before {
- content: "\f0f4";
-}
-.fa-cutlery:before {
- content: "\f0f5";
-}
-.fa-file-text-o:before {
- content: "\f0f6";
-}
-.fa-building-o:before {
- content: "\f0f7";
-}
-.fa-hospital-o:before {
- content: "\f0f8";
-}
-.fa-ambulance:before {
- content: "\f0f9";
-}
-.fa-medkit:before {
- content: "\f0fa";
-}
-.fa-fighter-jet:before {
- content: "\f0fb";
-}
-.fa-beer:before {
- content: "\f0fc";
-}
-.fa-h-square:before {
- content: "\f0fd";
-}
-.fa-plus-square:before {
- content: "\f0fe";
-}
-.fa-angle-double-left:before {
- content: "\f100";
-}
-.fa-angle-double-right:before {
- content: "\f101";
-}
-.fa-angle-double-up:before {
- content: "\f102";
-}
-.fa-angle-double-down:before {
- content: "\f103";
-}
-.fa-angle-left:before {
- content: "\f104";
-}
-.fa-angle-right:before {
- content: "\f105";
-}
-.fa-angle-up:before {
- content: "\f106";
-}
-.fa-angle-down:before {
- content: "\f107";
-}
-.fa-desktop:before {
- content: "\f108";
-}
-.fa-laptop:before {
- content: "\f109";
-}
-.fa-tablet:before {
- content: "\f10a";
-}
-.fa-mobile-phone:before,
-.fa-mobile:before {
- content: "\f10b";
-}
-.fa-circle-o:before {
- content: "\f10c";
-}
-.fa-quote-left:before {
- content: "\f10d";
-}
-.fa-quote-right:before {
- content: "\f10e";
-}
-.fa-spinner:before {
- content: "\f110";
-}
-.fa-circle:before {
- content: "\f111";
-}
-.fa-mail-reply:before,
-.fa-reply:before {
- content: "\f112";
-}
-.fa-github-alt:before {
- content: "\f113";
-}
-.fa-folder-o:before {
- content: "\f114";
-}
-.fa-folder-open-o:before {
- content: "\f115";
-}
-.fa-smile-o:before {
- content: "\f118";
-}
-.fa-frown-o:before {
- content: "\f119";
-}
-.fa-meh-o:before {
- content: "\f11a";
-}
-.fa-gamepad:before {
- content: "\f11b";
-}
-.fa-keyboard-o:before {
- content: "\f11c";
-}
-.fa-flag-o:before {
- content: "\f11d";
-}
-.fa-flag-checkered:before {
- content: "\f11e";
-}
-.fa-terminal:before {
- content: "\f120";
-}
-.fa-code:before {
- content: "\f121";
-}
-.fa-mail-reply-all:before,
-.fa-reply-all:before {
- content: "\f122";
-}
-.fa-star-half-empty:before,
-.fa-star-half-full:before,
-.fa-star-half-o:before {
- content: "\f123";
-}
-.fa-location-arrow:before {
- content: "\f124";
-}
-.fa-crop:before {
- content: "\f125";
-}
-.fa-code-fork:before {
- content: "\f126";
-}
-.fa-unlink:before,
-.fa-chain-broken:before {
- content: "\f127";
-}
-.fa-question:before {
- content: "\f128";
-}
-.fa-info:before {
- content: "\f129";
-}
-.fa-exclamation:before {
- content: "\f12a";
-}
-.fa-superscript:before {
- content: "\f12b";
-}
-.fa-subscript:before {
- content: "\f12c";
-}
-.fa-eraser:before {
- content: "\f12d";
-}
-.fa-puzzle-piece:before {
- content: "\f12e";
-}
-.fa-microphone:before {
- content: "\f130";
-}
-.fa-microphone-slash:before {
- content: "\f131";
-}
-.fa-shield:before {
- content: "\f132";
-}
-.fa-calendar-o:before {
- content: "\f133";
-}
-.fa-fire-extinguisher:before {
- content: "\f134";
-}
-.fa-rocket:before {
- content: "\f135";
-}
-.fa-maxcdn:before {
- content: "\f136";
-}
-.fa-chevron-circle-left:before {
- content: "\f137";
-}
-.fa-chevron-circle-right:before {
- content: "\f138";
-}
-.fa-chevron-circle-up:before {
- content: "\f139";
-}
-.fa-chevron-circle-down:before {
- content: "\f13a";
-}
-.fa-html5:before {
- content: "\f13b";
-}
-.fa-css3:before {
- content: "\f13c";
-}
-.fa-anchor:before {
- content: "\f13d";
-}
-.fa-unlock-alt:before {
- content: "\f13e";
-}
-.fa-bullseye:before {
- content: "\f140";
-}
-.fa-ellipsis-h:before {
- content: "\f141";
-}
-.fa-ellipsis-v:before {
- content: "\f142";
-}
-.fa-rss-square:before {
- content: "\f143";
-}
-.fa-play-circle:before {
- content: "\f144";
-}
-.fa-ticket:before {
- content: "\f145";
-}
-.fa-minus-square:before {
- content: "\f146";
-}
-.fa-minus-square-o:before {
- content: "\f147";
-}
-.fa-level-up:before {
- content: "\f148";
-}
-.fa-level-down:before {
- content: "\f149";
-}
-.fa-check-square:before {
- content: "\f14a";
-}
-.fa-pencil-square:before {
- content: "\f14b";
-}
-.fa-external-link-square:before {
- content: "\f14c";
-}
-.fa-share-square:before {
- content: "\f14d";
-}
-.fa-compass:before {
- content: "\f14e";
-}
-.fa-toggle-down:before,
-.fa-caret-square-o-down:before {
- content: "\f150";
-}
-.fa-toggle-up:before,
-.fa-caret-square-o-up:before {
- content: "\f151";
-}
-.fa-toggle-right:before,
-.fa-caret-square-o-right:before {
- content: "\f152";
-}
-.fa-euro:before,
-.fa-eur:before {
- content: "\f153";
-}
-.fa-gbp:before {
- content: "\f154";
-}
-.fa-dollar:before,
-.fa-usd:before {
- content: "\f155";
-}
-.fa-rupee:before,
-.fa-inr:before {
- content: "\f156";
-}
-.fa-cny:before,
-.fa-rmb:before,
-.fa-yen:before,
-.fa-jpy:before {
- content: "\f157";
-}
-.fa-ruble:before,
-.fa-rouble:before,
-.fa-rub:before {
- content: "\f158";
-}
-.fa-won:before,
-.fa-krw:before {
- content: "\f159";
-}
-.fa-bitcoin:before,
-.fa-btc:before {
- content: "\f15a";
-}
-.fa-file:before {
- content: "\f15b";
-}
-.fa-file-text:before {
- content: "\f15c";
-}
-.fa-sort-alpha-asc:before {
- content: "\f15d";
-}
-.fa-sort-alpha-desc:before {
- content: "\f15e";
-}
-.fa-sort-amount-asc:before {
- content: "\f160";
-}
-.fa-sort-amount-desc:before {
- content: "\f161";
-}
-.fa-sort-numeric-asc:before {
- content: "\f162";
-}
-.fa-sort-numeric-desc:before {
- content: "\f163";
-}
-.fa-thumbs-up:before {
- content: "\f164";
-}
-.fa-thumbs-down:before {
- content: "\f165";
-}
-.fa-youtube-square:before {
- content: "\f166";
-}
-.fa-youtube:before {
- content: "\f167";
-}
-.fa-xing:before {
- content: "\f168";
-}
-.fa-xing-square:before {
- content: "\f169";
-}
-.fa-youtube-play:before {
- content: "\f16a";
-}
-.fa-dropbox:before {
- content: "\f16b";
-}
-.fa-stack-overflow:before {
- content: "\f16c";
-}
-.fa-instagram:before {
- content: "\f16d";
-}
-.fa-flickr:before {
- content: "\f16e";
-}
-.fa-adn:before {
- content: "\f170";
-}
-.fa-bitbucket:before {
- content: "\f171";
-}
-.fa-bitbucket-square:before {
- content: "\f172";
-}
-.fa-tumblr:before {
- content: "\f173";
-}
-.fa-tumblr-square:before {
- content: "\f174";
-}
-.fa-long-arrow-down:before {
- content: "\f175";
-}
-.fa-long-arrow-up:before {
- content: "\f176";
-}
-.fa-long-arrow-left:before {
- content: "\f177";
-}
-.fa-long-arrow-right:before {
- content: "\f178";
-}
-.fa-apple:before {
- content: "\f179";
-}
-.fa-windows:before {
- content: "\f17a";
-}
-.fa-android:before {
- content: "\f17b";
-}
-.fa-linux:before {
- content: "\f17c";
-}
-.fa-dribbble:before {
- content: "\f17d";
-}
-.fa-skype:before {
- content: "\f17e";
-}
-.fa-foursquare:before {
- content: "\f180";
-}
-.fa-trello:before {
- content: "\f181";
-}
-.fa-female:before {
- content: "\f182";
-}
-.fa-male:before {
- content: "\f183";
-}
-.fa-gittip:before,
-.fa-gratipay:before {
- content: "\f184";
-}
-.fa-sun-o:before {
- content: "\f185";
-}
-.fa-moon-o:before {
- content: "\f186";
-}
-.fa-archive:before {
- content: "\f187";
-}
-.fa-bug:before {
- content: "\f188";
-}
-.fa-vk:before {
- content: "\f189";
-}
-.fa-weibo:before {
- content: "\f18a";
-}
-.fa-renren:before {
- content: "\f18b";
-}
-.fa-pagelines:before {
- content: "\f18c";
-}
-.fa-stack-exchange:before {
- content: "\f18d";
-}
-.fa-arrow-circle-o-right:before {
- content: "\f18e";
-}
-.fa-arrow-circle-o-left:before {
- content: "\f190";
-}
-.fa-toggle-left:before,
-.fa-caret-square-o-left:before {
- content: "\f191";
-}
-.fa-dot-circle-o:before {
- content: "\f192";
-}
-.fa-wheelchair:before {
- content: "\f193";
-}
-.fa-vimeo-square:before {
- content: "\f194";
-}
-.fa-turkish-lira:before,
-.fa-try:before {
- content: "\f195";
-}
-.fa-plus-square-o:before {
- content: "\f196";
-}
-.fa-space-shuttle:before {
- content: "\f197";
-}
-.fa-slack:before {
- content: "\f198";
-}
-.fa-envelope-square:before {
- content: "\f199";
-}
-.fa-wordpress:before {
- content: "\f19a";
-}
-.fa-openid:before {
- content: "\f19b";
-}
-.fa-institution:before,
-.fa-bank:before,
-.fa-university:before {
- content: "\f19c";
-}
-.fa-mortar-board:before,
-.fa-graduation-cap:before {
- content: "\f19d";
-}
-.fa-yahoo:before {
- content: "\f19e";
-}
-.fa-google:before {
- content: "\f1a0";
-}
-.fa-reddit:before {
- content: "\f1a1";
-}
-.fa-reddit-square:before {
- content: "\f1a2";
-}
-.fa-stumbleupon-circle:before {
- content: "\f1a3";
-}
-.fa-stumbleupon:before {
- content: "\f1a4";
-}
-.fa-delicious:before {
- content: "\f1a5";
-}
-.fa-digg:before {
- content: "\f1a6";
-}
-.fa-pied-piper:before {
- content: "\f1a7";
-}
-.fa-pied-piper-alt:before {
- content: "\f1a8";
-}
-.fa-drupal:before {
- content: "\f1a9";
-}
-.fa-joomla:before {
- content: "\f1aa";
-}
-.fa-language:before {
- content: "\f1ab";
-}
-.fa-fax:before {
- content: "\f1ac";
-}
-.fa-building:before {
- content: "\f1ad";
-}
-.fa-child:before {
- content: "\f1ae";
-}
-.fa-paw:before {
- content: "\f1b0";
-}
-.fa-spoon:before {
- content: "\f1b1";
-}
-.fa-cube:before {
- content: "\f1b2";
-}
-.fa-cubes:before {
- content: "\f1b3";
-}
-.fa-behance:before {
- content: "\f1b4";
-}
-.fa-behance-square:before {
- content: "\f1b5";
-}
-.fa-steam:before {
- content: "\f1b6";
-}
-.fa-steam-square:before {
- content: "\f1b7";
-}
-.fa-recycle:before {
- content: "\f1b8";
-}
-.fa-automobile:before,
-.fa-car:before {
- content: "\f1b9";
-}
-.fa-cab:before,
-.fa-taxi:before {
- content: "\f1ba";
-}
-.fa-tree:before {
- content: "\f1bb";
-}
-.fa-spotify:before {
- content: "\f1bc";
-}
-.fa-deviantart:before {
- content: "\f1bd";
-}
-.fa-soundcloud:before {
- content: "\f1be";
-}
-.fa-database:before {
- content: "\f1c0";
-}
-.fa-file-pdf-o:before {
- content: "\f1c1";
-}
-.fa-file-word-o:before {
- content: "\f1c2";
-}
-.fa-file-excel-o:before {
- content: "\f1c3";
-}
-.fa-file-powerpoint-o:before {
- content: "\f1c4";
-}
-.fa-file-photo-o:before,
-.fa-file-picture-o:before,
-.fa-file-image-o:before {
- content: "\f1c5";
-}
-.fa-file-zip-o:before,
-.fa-file-archive-o:before {
- content: "\f1c6";
-}
-.fa-file-sound-o:before,
-.fa-file-audio-o:before {
- content: "\f1c7";
-}
-.fa-file-movie-o:before,
-.fa-file-video-o:before {
- content: "\f1c8";
-}
-.fa-file-code-o:before {
- content: "\f1c9";
-}
-.fa-vine:before {
- content: "\f1ca";
-}
-.fa-codepen:before {
- content: "\f1cb";
-}
-.fa-jsfiddle:before {
- content: "\f1cc";
-}
-.fa-life-bouy:before,
-.fa-life-buoy:before,
-.fa-life-saver:before,
-.fa-support:before,
-.fa-life-ring:before {
- content: "\f1cd";
-}
-.fa-circle-o-notch:before {
- content: "\f1ce";
-}
-.fa-ra:before,
-.fa-rebel:before {
- content: "\f1d0";
-}
-.fa-ge:before,
-.fa-empire:before {
- content: "\f1d1";
-}
-.fa-git-square:before {
- content: "\f1d2";
-}
-.fa-git:before {
- content: "\f1d3";
-}
-.fa-hacker-news:before {
- content: "\f1d4";
-}
-.fa-tencent-weibo:before {
- content: "\f1d5";
-}
-.fa-qq:before {
- content: "\f1d6";
-}
-.fa-wechat:before,
-.fa-weixin:before {
- content: "\f1d7";
-}
-.fa-send:before,
-.fa-paper-plane:before {
- content: "\f1d8";
-}
-.fa-send-o:before,
-.fa-paper-plane-o:before {
- content: "\f1d9";
-}
-.fa-history:before {
- content: "\f1da";
-}
-.fa-genderless:before,
-.fa-circle-thin:before {
- content: "\f1db";
-}
-.fa-header:before {
- content: "\f1dc";
-}
-.fa-paragraph:before {
- content: "\f1dd";
-}
-.fa-sliders:before {
- content: "\f1de";
-}
-.fa-share-alt:before {
- content: "\f1e0";
-}
-.fa-share-alt-square:before {
- content: "\f1e1";
-}
-.fa-bomb:before {
- content: "\f1e2";
-}
-.fa-soccer-ball-o:before,
-.fa-futbol-o:before {
- content: "\f1e3";
-}
-.fa-tty:before {
- content: "\f1e4";
-}
-.fa-binoculars:before {
- content: "\f1e5";
-}
-.fa-plug:before {
- content: "\f1e6";
-}
-.fa-slideshare:before {
- content: "\f1e7";
-}
-.fa-twitch:before {
- content: "\f1e8";
-}
-.fa-yelp:before {
- content: "\f1e9";
-}
-.fa-newspaper-o:before {
- content: "\f1ea";
-}
-.fa-wifi:before {
- content: "\f1eb";
-}
-.fa-calculator:before {
- content: "\f1ec";
-}
-.fa-paypal:before {
- content: "\f1ed";
-}
-.fa-google-wallet:before {
- content: "\f1ee";
-}
-.fa-cc-visa:before {
- content: "\f1f0";
-}
-.fa-cc-mastercard:before {
- content: "\f1f1";
-}
-.fa-cc-discover:before {
- content: "\f1f2";
-}
-.fa-cc-amex:before {
- content: "\f1f3";
-}
-.fa-cc-paypal:before {
- content: "\f1f4";
-}
-.fa-cc-stripe:before {
- content: "\f1f5";
-}
-.fa-bell-slash:before {
- content: "\f1f6";
-}
-.fa-bell-slash-o:before {
- content: "\f1f7";
-}
-.fa-trash:before {
- content: "\f1f8";
-}
-.fa-copyright:before {
- content: "\f1f9";
-}
-.fa-at:before {
- content: "\f1fa";
-}
-.fa-eyedropper:before {
- content: "\f1fb";
-}
-.fa-paint-brush:before {
- content: "\f1fc";
-}
-.fa-birthday-cake:before {
- content: "\f1fd";
-}
-.fa-area-chart:before {
- content: "\f1fe";
-}
-.fa-pie-chart:before {
- content: "\f200";
-}
-.fa-line-chart:before {
- content: "\f201";
-}
-.fa-lastfm:before {
- content: "\f202";
-}
-.fa-lastfm-square:before {
- content: "\f203";
-}
-.fa-toggle-off:before {
- content: "\f204";
-}
-.fa-toggle-on:before {
- content: "\f205";
-}
-.fa-bicycle:before {
- content: "\f206";
-}
-.fa-bus:before {
- content: "\f207";
-}
-.fa-ioxhost:before {
- content: "\f208";
-}
-.fa-angellist:before {
- content: "\f209";
-}
-.fa-cc:before {
- content: "\f20a";
-}
-.fa-shekel:before,
-.fa-sheqel:before,
-.fa-ils:before {
- content: "\f20b";
-}
-.fa-meanpath:before {
- content: "\f20c";
-}
-.fa-buysellads:before {
- content: "\f20d";
-}
-.fa-connectdevelop:before {
- content: "\f20e";
-}
-.fa-dashcube:before {
- content: "\f210";
-}
-.fa-forumbee:before {
- content: "\f211";
-}
-.fa-leanpub:before {
- content: "\f212";
-}
-.fa-sellsy:before {
- content: "\f213";
-}
-.fa-shirtsinbulk:before {
- content: "\f214";
-}
-.fa-simplybuilt:before {
- content: "\f215";
-}
-.fa-skyatlas:before {
- content: "\f216";
-}
-.fa-cart-plus:before {
- content: "\f217";
-}
-.fa-cart-arrow-down:before {
- content: "\f218";
-}
-.fa-diamond:before {
- content: "\f219";
-}
-.fa-ship:before {
- content: "\f21a";
-}
-.fa-user-secret:before {
- content: "\f21b";
-}
-.fa-motorcycle:before {
- content: "\f21c";
-}
-.fa-street-view:before {
- content: "\f21d";
-}
-.fa-heartbeat:before {
- content: "\f21e";
-}
-.fa-venus:before {
- content: "\f221";
-}
-.fa-mars:before {
- content: "\f222";
-}
-.fa-mercury:before {
- content: "\f223";
-}
-.fa-transgender:before {
- content: "\f224";
-}
-.fa-transgender-alt:before {
- content: "\f225";
-}
-.fa-venus-double:before {
- content: "\f226";
-}
-.fa-mars-double:before {
- content: "\f227";
-}
-.fa-venus-mars:before {
- content: "\f228";
-}
-.fa-mars-stroke:before {
- content: "\f229";
-}
-.fa-mars-stroke-v:before {
- content: "\f22a";
-}
-.fa-mars-stroke-h:before {
- content: "\f22b";
-}
-.fa-neuter:before {
- content: "\f22c";
-}
-.fa-facebook-official:before {
- content: "\f230";
-}
-.fa-pinterest-p:before {
- content: "\f231";
-}
-.fa-whatsapp:before {
- content: "\f232";
-}
-.fa-server:before {
- content: "\f233";
-}
-.fa-user-plus:before {
- content: "\f234";
-}
-.fa-user-times:before {
- content: "\f235";
-}
-.fa-hotel:before,
-.fa-bed:before {
- content: "\f236";
-}
-.fa-viacoin:before {
- content: "\f237";
-}
-.fa-train:before {
- content: "\f238";
-}
-.fa-subway:before {
- content: "\f239";
-}
-.fa-medium:before {
- content: "\f23a";
-}
diff --git a/presentation/contrib/font-awesome-4.3.0/css/font-awesome.min.css b/presentation/contrib/font-awesome-4.3.0/css/font-awesome.min.css
deleted file mode 100644
index 24fcc04c4..000000000
--- a/presentation/contrib/font-awesome-4.3.0/css/font-awesome.min.css
+++ /dev/null
@@ -1,4 +0,0 @@
-/*!
- * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome
- * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
- */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.3.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.3.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.3.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.3.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.3.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.3.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;transform:translate(0, 0)}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-genderless:before,.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}
\ No newline at end of file
diff --git a/presentation/contrib/font-awesome-4.3.0/fonts/FontAwesome.otf b/presentation/contrib/font-awesome-4.3.0/fonts/FontAwesome.otf
deleted file mode 100644
index f7936cc1e..000000000
Binary files a/presentation/contrib/font-awesome-4.3.0/fonts/FontAwesome.otf and /dev/null differ
diff --git a/presentation/contrib/font-awesome-4.3.0/fonts/fontawesome-webfont.eot b/presentation/contrib/font-awesome-4.3.0/fonts/fontawesome-webfont.eot
deleted file mode 100644
index 33b2bb800..000000000
Binary files a/presentation/contrib/font-awesome-4.3.0/fonts/fontawesome-webfont.eot and /dev/null differ
diff --git a/presentation/contrib/font-awesome-4.3.0/fonts/fontawesome-webfont.svg b/presentation/contrib/font-awesome-4.3.0/fonts/fontawesome-webfont.svg
deleted file mode 100644
index 1ee89d436..000000000
--- a/presentation/contrib/font-awesome-4.3.0/fonts/fontawesome-webfont.svg
+++ /dev/null
@@ -1,565 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/presentation/contrib/font-awesome-4.3.0/fonts/fontawesome-webfont.ttf b/presentation/contrib/font-awesome-4.3.0/fonts/fontawesome-webfont.ttf
deleted file mode 100644
index ed9372f8e..000000000
Binary files a/presentation/contrib/font-awesome-4.3.0/fonts/fontawesome-webfont.ttf and /dev/null differ
diff --git a/presentation/contrib/font-awesome-4.3.0/fonts/fontawesome-webfont.woff b/presentation/contrib/font-awesome-4.3.0/fonts/fontawesome-webfont.woff
deleted file mode 100644
index 8b280b98f..000000000
Binary files a/presentation/contrib/font-awesome-4.3.0/fonts/fontawesome-webfont.woff and /dev/null differ
diff --git a/presentation/contrib/font-awesome-4.3.0/fonts/fontawesome-webfont.woff2 b/presentation/contrib/font-awesome-4.3.0/fonts/fontawesome-webfont.woff2
deleted file mode 100644
index 3311d5851..000000000
Binary files a/presentation/contrib/font-awesome-4.3.0/fonts/fontawesome-webfont.woff2 and /dev/null differ
diff --git a/presentation/contrib/font-awesome-4.3.0/less/animated.less b/presentation/contrib/font-awesome-4.3.0/less/animated.less
deleted file mode 100644
index 66ad52a5b..000000000
--- a/presentation/contrib/font-awesome-4.3.0/less/animated.less
+++ /dev/null
@@ -1,34 +0,0 @@
-// Animated Icons
-// --------------------------
-
-.@{fa-css-prefix}-spin {
- -webkit-animation: fa-spin 2s infinite linear;
- animation: fa-spin 2s infinite linear;
-}
-
-.@{fa-css-prefix}-pulse {
- -webkit-animation: fa-spin 1s infinite steps(8);
- animation: fa-spin 1s infinite steps(8);
-}
-
-@-webkit-keyframes fa-spin {
- 0% {
- -webkit-transform: rotate(0deg);
- transform: rotate(0deg);
- }
- 100% {
- -webkit-transform: rotate(359deg);
- transform: rotate(359deg);
- }
-}
-
-@keyframes fa-spin {
- 0% {
- -webkit-transform: rotate(0deg);
- transform: rotate(0deg);
- }
- 100% {
- -webkit-transform: rotate(359deg);
- transform: rotate(359deg);
- }
-}
diff --git a/presentation/contrib/font-awesome-4.3.0/less/bordered-pulled.less b/presentation/contrib/font-awesome-4.3.0/less/bordered-pulled.less
deleted file mode 100644
index 0c90eb567..000000000
--- a/presentation/contrib/font-awesome-4.3.0/less/bordered-pulled.less
+++ /dev/null
@@ -1,16 +0,0 @@
-// Bordered & Pulled
-// -------------------------
-
-.@{fa-css-prefix}-border {
- padding: .2em .25em .15em;
- border: solid .08em @fa-border-color;
- border-radius: .1em;
-}
-
-.pull-right { float: right; }
-.pull-left { float: left; }
-
-.@{fa-css-prefix} {
- &.pull-left { margin-right: .3em; }
- &.pull-right { margin-left: .3em; }
-}
diff --git a/presentation/contrib/font-awesome-4.3.0/less/core.less b/presentation/contrib/font-awesome-4.3.0/less/core.less
deleted file mode 100644
index f814f1e17..000000000
--- a/presentation/contrib/font-awesome-4.3.0/less/core.less
+++ /dev/null
@@ -1,13 +0,0 @@
-// Base Class Definition
-// -------------------------
-
-.@{fa-css-prefix} {
- display: inline-block;
- font: normal normal normal @fa-font-size-base/1 FontAwesome; // shortening font declaration
- font-size: inherit; // can't have font-size inherit on line above, so need to override
- text-rendering: auto; // optimizelegibility throws things off #1094
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- transform: translate(0, 0); // ensures no half-pixel rendering in firefox
-
-}
diff --git a/presentation/contrib/font-awesome-4.3.0/less/fixed-width.less b/presentation/contrib/font-awesome-4.3.0/less/fixed-width.less
deleted file mode 100644
index 110289f2f..000000000
--- a/presentation/contrib/font-awesome-4.3.0/less/fixed-width.less
+++ /dev/null
@@ -1,6 +0,0 @@
-// Fixed Width Icons
-// -------------------------
-.@{fa-css-prefix}-fw {
- width: (18em / 14);
- text-align: center;
-}
diff --git a/presentation/contrib/font-awesome-4.3.0/less/font-awesome.less b/presentation/contrib/font-awesome-4.3.0/less/font-awesome.less
deleted file mode 100644
index 1f45c63d1..000000000
--- a/presentation/contrib/font-awesome-4.3.0/less/font-awesome.less
+++ /dev/null
@@ -1,17 +0,0 @@
-/*!
- * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome
- * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
- */
-
-@import "variables.less";
-@import "mixins.less";
-@import "path.less";
-@import "core.less";
-@import "larger.less";
-@import "fixed-width.less";
-@import "list.less";
-@import "bordered-pulled.less";
-@import "animated.less";
-@import "rotated-flipped.less";
-@import "stacked.less";
-@import "icons.less";
diff --git a/presentation/contrib/font-awesome-4.3.0/less/icons.less b/presentation/contrib/font-awesome-4.3.0/less/icons.less
deleted file mode 100644
index c265de5a6..000000000
--- a/presentation/contrib/font-awesome-4.3.0/less/icons.less
+++ /dev/null
@@ -1,596 +0,0 @@
-/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
- readers do not read off random characters that represent icons */
-
-.@{fa-css-prefix}-glass:before { content: @fa-var-glass; }
-.@{fa-css-prefix}-music:before { content: @fa-var-music; }
-.@{fa-css-prefix}-search:before { content: @fa-var-search; }
-.@{fa-css-prefix}-envelope-o:before { content: @fa-var-envelope-o; }
-.@{fa-css-prefix}-heart:before { content: @fa-var-heart; }
-.@{fa-css-prefix}-star:before { content: @fa-var-star; }
-.@{fa-css-prefix}-star-o:before { content: @fa-var-star-o; }
-.@{fa-css-prefix}-user:before { content: @fa-var-user; }
-.@{fa-css-prefix}-film:before { content: @fa-var-film; }
-.@{fa-css-prefix}-th-large:before { content: @fa-var-th-large; }
-.@{fa-css-prefix}-th:before { content: @fa-var-th; }
-.@{fa-css-prefix}-th-list:before { content: @fa-var-th-list; }
-.@{fa-css-prefix}-check:before { content: @fa-var-check; }
-.@{fa-css-prefix}-remove:before,
-.@{fa-css-prefix}-close:before,
-.@{fa-css-prefix}-times:before { content: @fa-var-times; }
-.@{fa-css-prefix}-search-plus:before { content: @fa-var-search-plus; }
-.@{fa-css-prefix}-search-minus:before { content: @fa-var-search-minus; }
-.@{fa-css-prefix}-power-off:before { content: @fa-var-power-off; }
-.@{fa-css-prefix}-signal:before { content: @fa-var-signal; }
-.@{fa-css-prefix}-gear:before,
-.@{fa-css-prefix}-cog:before { content: @fa-var-cog; }
-.@{fa-css-prefix}-trash-o:before { content: @fa-var-trash-o; }
-.@{fa-css-prefix}-home:before { content: @fa-var-home; }
-.@{fa-css-prefix}-file-o:before { content: @fa-var-file-o; }
-.@{fa-css-prefix}-clock-o:before { content: @fa-var-clock-o; }
-.@{fa-css-prefix}-road:before { content: @fa-var-road; }
-.@{fa-css-prefix}-download:before { content: @fa-var-download; }
-.@{fa-css-prefix}-arrow-circle-o-down:before { content: @fa-var-arrow-circle-o-down; }
-.@{fa-css-prefix}-arrow-circle-o-up:before { content: @fa-var-arrow-circle-o-up; }
-.@{fa-css-prefix}-inbox:before { content: @fa-var-inbox; }
-.@{fa-css-prefix}-play-circle-o:before { content: @fa-var-play-circle-o; }
-.@{fa-css-prefix}-rotate-right:before,
-.@{fa-css-prefix}-repeat:before { content: @fa-var-repeat; }
-.@{fa-css-prefix}-refresh:before { content: @fa-var-refresh; }
-.@{fa-css-prefix}-list-alt:before { content: @fa-var-list-alt; }
-.@{fa-css-prefix}-lock:before { content: @fa-var-lock; }
-.@{fa-css-prefix}-flag:before { content: @fa-var-flag; }
-.@{fa-css-prefix}-headphones:before { content: @fa-var-headphones; }
-.@{fa-css-prefix}-volume-off:before { content: @fa-var-volume-off; }
-.@{fa-css-prefix}-volume-down:before { content: @fa-var-volume-down; }
-.@{fa-css-prefix}-volume-up:before { content: @fa-var-volume-up; }
-.@{fa-css-prefix}-qrcode:before { content: @fa-var-qrcode; }
-.@{fa-css-prefix}-barcode:before { content: @fa-var-barcode; }
-.@{fa-css-prefix}-tag:before { content: @fa-var-tag; }
-.@{fa-css-prefix}-tags:before { content: @fa-var-tags; }
-.@{fa-css-prefix}-book:before { content: @fa-var-book; }
-.@{fa-css-prefix}-bookmark:before { content: @fa-var-bookmark; }
-.@{fa-css-prefix}-print:before { content: @fa-var-print; }
-.@{fa-css-prefix}-camera:before { content: @fa-var-camera; }
-.@{fa-css-prefix}-font:before { content: @fa-var-font; }
-.@{fa-css-prefix}-bold:before { content: @fa-var-bold; }
-.@{fa-css-prefix}-italic:before { content: @fa-var-italic; }
-.@{fa-css-prefix}-text-height:before { content: @fa-var-text-height; }
-.@{fa-css-prefix}-text-width:before { content: @fa-var-text-width; }
-.@{fa-css-prefix}-align-left:before { content: @fa-var-align-left; }
-.@{fa-css-prefix}-align-center:before { content: @fa-var-align-center; }
-.@{fa-css-prefix}-align-right:before { content: @fa-var-align-right; }
-.@{fa-css-prefix}-align-justify:before { content: @fa-var-align-justify; }
-.@{fa-css-prefix}-list:before { content: @fa-var-list; }
-.@{fa-css-prefix}-dedent:before,
-.@{fa-css-prefix}-outdent:before { content: @fa-var-outdent; }
-.@{fa-css-prefix}-indent:before { content: @fa-var-indent; }
-.@{fa-css-prefix}-video-camera:before { content: @fa-var-video-camera; }
-.@{fa-css-prefix}-photo:before,
-.@{fa-css-prefix}-image:before,
-.@{fa-css-prefix}-picture-o:before { content: @fa-var-picture-o; }
-.@{fa-css-prefix}-pencil:before { content: @fa-var-pencil; }
-.@{fa-css-prefix}-map-marker:before { content: @fa-var-map-marker; }
-.@{fa-css-prefix}-adjust:before { content: @fa-var-adjust; }
-.@{fa-css-prefix}-tint:before { content: @fa-var-tint; }
-.@{fa-css-prefix}-edit:before,
-.@{fa-css-prefix}-pencil-square-o:before { content: @fa-var-pencil-square-o; }
-.@{fa-css-prefix}-share-square-o:before { content: @fa-var-share-square-o; }
-.@{fa-css-prefix}-check-square-o:before { content: @fa-var-check-square-o; }
-.@{fa-css-prefix}-arrows:before { content: @fa-var-arrows; }
-.@{fa-css-prefix}-step-backward:before { content: @fa-var-step-backward; }
-.@{fa-css-prefix}-fast-backward:before { content: @fa-var-fast-backward; }
-.@{fa-css-prefix}-backward:before { content: @fa-var-backward; }
-.@{fa-css-prefix}-play:before { content: @fa-var-play; }
-.@{fa-css-prefix}-pause:before { content: @fa-var-pause; }
-.@{fa-css-prefix}-stop:before { content: @fa-var-stop; }
-.@{fa-css-prefix}-forward:before { content: @fa-var-forward; }
-.@{fa-css-prefix}-fast-forward:before { content: @fa-var-fast-forward; }
-.@{fa-css-prefix}-step-forward:before { content: @fa-var-step-forward; }
-.@{fa-css-prefix}-eject:before { content: @fa-var-eject; }
-.@{fa-css-prefix}-chevron-left:before { content: @fa-var-chevron-left; }
-.@{fa-css-prefix}-chevron-right:before { content: @fa-var-chevron-right; }
-.@{fa-css-prefix}-plus-circle:before { content: @fa-var-plus-circle; }
-.@{fa-css-prefix}-minus-circle:before { content: @fa-var-minus-circle; }
-.@{fa-css-prefix}-times-circle:before { content: @fa-var-times-circle; }
-.@{fa-css-prefix}-check-circle:before { content: @fa-var-check-circle; }
-.@{fa-css-prefix}-question-circle:before { content: @fa-var-question-circle; }
-.@{fa-css-prefix}-info-circle:before { content: @fa-var-info-circle; }
-.@{fa-css-prefix}-crosshairs:before { content: @fa-var-crosshairs; }
-.@{fa-css-prefix}-times-circle-o:before { content: @fa-var-times-circle-o; }
-.@{fa-css-prefix}-check-circle-o:before { content: @fa-var-check-circle-o; }
-.@{fa-css-prefix}-ban:before { content: @fa-var-ban; }
-.@{fa-css-prefix}-arrow-left:before { content: @fa-var-arrow-left; }
-.@{fa-css-prefix}-arrow-right:before { content: @fa-var-arrow-right; }
-.@{fa-css-prefix}-arrow-up:before { content: @fa-var-arrow-up; }
-.@{fa-css-prefix}-arrow-down:before { content: @fa-var-arrow-down; }
-.@{fa-css-prefix}-mail-forward:before,
-.@{fa-css-prefix}-share:before { content: @fa-var-share; }
-.@{fa-css-prefix}-expand:before { content: @fa-var-expand; }
-.@{fa-css-prefix}-compress:before { content: @fa-var-compress; }
-.@{fa-css-prefix}-plus:before { content: @fa-var-plus; }
-.@{fa-css-prefix}-minus:before { content: @fa-var-minus; }
-.@{fa-css-prefix}-asterisk:before { content: @fa-var-asterisk; }
-.@{fa-css-prefix}-exclamation-circle:before { content: @fa-var-exclamation-circle; }
-.@{fa-css-prefix}-gift:before { content: @fa-var-gift; }
-.@{fa-css-prefix}-leaf:before { content: @fa-var-leaf; }
-.@{fa-css-prefix}-fire:before { content: @fa-var-fire; }
-.@{fa-css-prefix}-eye:before { content: @fa-var-eye; }
-.@{fa-css-prefix}-eye-slash:before { content: @fa-var-eye-slash; }
-.@{fa-css-prefix}-warning:before,
-.@{fa-css-prefix}-exclamation-triangle:before { content: @fa-var-exclamation-triangle; }
-.@{fa-css-prefix}-plane:before { content: @fa-var-plane; }
-.@{fa-css-prefix}-calendar:before { content: @fa-var-calendar; }
-.@{fa-css-prefix}-random:before { content: @fa-var-random; }
-.@{fa-css-prefix}-comment:before { content: @fa-var-comment; }
-.@{fa-css-prefix}-magnet:before { content: @fa-var-magnet; }
-.@{fa-css-prefix}-chevron-up:before { content: @fa-var-chevron-up; }
-.@{fa-css-prefix}-chevron-down:before { content: @fa-var-chevron-down; }
-.@{fa-css-prefix}-retweet:before { content: @fa-var-retweet; }
-.@{fa-css-prefix}-shopping-cart:before { content: @fa-var-shopping-cart; }
-.@{fa-css-prefix}-folder:before { content: @fa-var-folder; }
-.@{fa-css-prefix}-folder-open:before { content: @fa-var-folder-open; }
-.@{fa-css-prefix}-arrows-v:before { content: @fa-var-arrows-v; }
-.@{fa-css-prefix}-arrows-h:before { content: @fa-var-arrows-h; }
-.@{fa-css-prefix}-bar-chart-o:before,
-.@{fa-css-prefix}-bar-chart:before { content: @fa-var-bar-chart; }
-.@{fa-css-prefix}-twitter-square:before { content: @fa-var-twitter-square; }
-.@{fa-css-prefix}-facebook-square:before { content: @fa-var-facebook-square; }
-.@{fa-css-prefix}-camera-retro:before { content: @fa-var-camera-retro; }
-.@{fa-css-prefix}-key:before { content: @fa-var-key; }
-.@{fa-css-prefix}-gears:before,
-.@{fa-css-prefix}-cogs:before { content: @fa-var-cogs; }
-.@{fa-css-prefix}-comments:before { content: @fa-var-comments; }
-.@{fa-css-prefix}-thumbs-o-up:before { content: @fa-var-thumbs-o-up; }
-.@{fa-css-prefix}-thumbs-o-down:before { content: @fa-var-thumbs-o-down; }
-.@{fa-css-prefix}-star-half:before { content: @fa-var-star-half; }
-.@{fa-css-prefix}-heart-o:before { content: @fa-var-heart-o; }
-.@{fa-css-prefix}-sign-out:before { content: @fa-var-sign-out; }
-.@{fa-css-prefix}-linkedin-square:before { content: @fa-var-linkedin-square; }
-.@{fa-css-prefix}-thumb-tack:before { content: @fa-var-thumb-tack; }
-.@{fa-css-prefix}-external-link:before { content: @fa-var-external-link; }
-.@{fa-css-prefix}-sign-in:before { content: @fa-var-sign-in; }
-.@{fa-css-prefix}-trophy:before { content: @fa-var-trophy; }
-.@{fa-css-prefix}-github-square:before { content: @fa-var-github-square; }
-.@{fa-css-prefix}-upload:before { content: @fa-var-upload; }
-.@{fa-css-prefix}-lemon-o:before { content: @fa-var-lemon-o; }
-.@{fa-css-prefix}-phone:before { content: @fa-var-phone; }
-.@{fa-css-prefix}-square-o:before { content: @fa-var-square-o; }
-.@{fa-css-prefix}-bookmark-o:before { content: @fa-var-bookmark-o; }
-.@{fa-css-prefix}-phone-square:before { content: @fa-var-phone-square; }
-.@{fa-css-prefix}-twitter:before { content: @fa-var-twitter; }
-.@{fa-css-prefix}-facebook-f:before,
-.@{fa-css-prefix}-facebook:before { content: @fa-var-facebook; }
-.@{fa-css-prefix}-github:before { content: @fa-var-github; }
-.@{fa-css-prefix}-unlock:before { content: @fa-var-unlock; }
-.@{fa-css-prefix}-credit-card:before { content: @fa-var-credit-card; }
-.@{fa-css-prefix}-rss:before { content: @fa-var-rss; }
-.@{fa-css-prefix}-hdd-o:before { content: @fa-var-hdd-o; }
-.@{fa-css-prefix}-bullhorn:before { content: @fa-var-bullhorn; }
-.@{fa-css-prefix}-bell:before { content: @fa-var-bell; }
-.@{fa-css-prefix}-certificate:before { content: @fa-var-certificate; }
-.@{fa-css-prefix}-hand-o-right:before { content: @fa-var-hand-o-right; }
-.@{fa-css-prefix}-hand-o-left:before { content: @fa-var-hand-o-left; }
-.@{fa-css-prefix}-hand-o-up:before { content: @fa-var-hand-o-up; }
-.@{fa-css-prefix}-hand-o-down:before { content: @fa-var-hand-o-down; }
-.@{fa-css-prefix}-arrow-circle-left:before { content: @fa-var-arrow-circle-left; }
-.@{fa-css-prefix}-arrow-circle-right:before { content: @fa-var-arrow-circle-right; }
-.@{fa-css-prefix}-arrow-circle-up:before { content: @fa-var-arrow-circle-up; }
-.@{fa-css-prefix}-arrow-circle-down:before { content: @fa-var-arrow-circle-down; }
-.@{fa-css-prefix}-globe:before { content: @fa-var-globe; }
-.@{fa-css-prefix}-wrench:before { content: @fa-var-wrench; }
-.@{fa-css-prefix}-tasks:before { content: @fa-var-tasks; }
-.@{fa-css-prefix}-filter:before { content: @fa-var-filter; }
-.@{fa-css-prefix}-briefcase:before { content: @fa-var-briefcase; }
-.@{fa-css-prefix}-arrows-alt:before { content: @fa-var-arrows-alt; }
-.@{fa-css-prefix}-group:before,
-.@{fa-css-prefix}-users:before { content: @fa-var-users; }
-.@{fa-css-prefix}-chain:before,
-.@{fa-css-prefix}-link:before { content: @fa-var-link; }
-.@{fa-css-prefix}-cloud:before { content: @fa-var-cloud; }
-.@{fa-css-prefix}-flask:before { content: @fa-var-flask; }
-.@{fa-css-prefix}-cut:before,
-.@{fa-css-prefix}-scissors:before { content: @fa-var-scissors; }
-.@{fa-css-prefix}-copy:before,
-.@{fa-css-prefix}-files-o:before { content: @fa-var-files-o; }
-.@{fa-css-prefix}-paperclip:before { content: @fa-var-paperclip; }
-.@{fa-css-prefix}-save:before,
-.@{fa-css-prefix}-floppy-o:before { content: @fa-var-floppy-o; }
-.@{fa-css-prefix}-square:before { content: @fa-var-square; }
-.@{fa-css-prefix}-navicon:before,
-.@{fa-css-prefix}-reorder:before,
-.@{fa-css-prefix}-bars:before { content: @fa-var-bars; }
-.@{fa-css-prefix}-list-ul:before { content: @fa-var-list-ul; }
-.@{fa-css-prefix}-list-ol:before { content: @fa-var-list-ol; }
-.@{fa-css-prefix}-strikethrough:before { content: @fa-var-strikethrough; }
-.@{fa-css-prefix}-underline:before { content: @fa-var-underline; }
-.@{fa-css-prefix}-table:before { content: @fa-var-table; }
-.@{fa-css-prefix}-magic:before { content: @fa-var-magic; }
-.@{fa-css-prefix}-truck:before { content: @fa-var-truck; }
-.@{fa-css-prefix}-pinterest:before { content: @fa-var-pinterest; }
-.@{fa-css-prefix}-pinterest-square:before { content: @fa-var-pinterest-square; }
-.@{fa-css-prefix}-google-plus-square:before { content: @fa-var-google-plus-square; }
-.@{fa-css-prefix}-google-plus:before { content: @fa-var-google-plus; }
-.@{fa-css-prefix}-money:before { content: @fa-var-money; }
-.@{fa-css-prefix}-caret-down:before { content: @fa-var-caret-down; }
-.@{fa-css-prefix}-caret-up:before { content: @fa-var-caret-up; }
-.@{fa-css-prefix}-caret-left:before { content: @fa-var-caret-left; }
-.@{fa-css-prefix}-caret-right:before { content: @fa-var-caret-right; }
-.@{fa-css-prefix}-columns:before { content: @fa-var-columns; }
-.@{fa-css-prefix}-unsorted:before,
-.@{fa-css-prefix}-sort:before { content: @fa-var-sort; }
-.@{fa-css-prefix}-sort-down:before,
-.@{fa-css-prefix}-sort-desc:before { content: @fa-var-sort-desc; }
-.@{fa-css-prefix}-sort-up:before,
-.@{fa-css-prefix}-sort-asc:before { content: @fa-var-sort-asc; }
-.@{fa-css-prefix}-envelope:before { content: @fa-var-envelope; }
-.@{fa-css-prefix}-linkedin:before { content: @fa-var-linkedin; }
-.@{fa-css-prefix}-rotate-left:before,
-.@{fa-css-prefix}-undo:before { content: @fa-var-undo; }
-.@{fa-css-prefix}-legal:before,
-.@{fa-css-prefix}-gavel:before { content: @fa-var-gavel; }
-.@{fa-css-prefix}-dashboard:before,
-.@{fa-css-prefix}-tachometer:before { content: @fa-var-tachometer; }
-.@{fa-css-prefix}-comment-o:before { content: @fa-var-comment-o; }
-.@{fa-css-prefix}-comments-o:before { content: @fa-var-comments-o; }
-.@{fa-css-prefix}-flash:before,
-.@{fa-css-prefix}-bolt:before { content: @fa-var-bolt; }
-.@{fa-css-prefix}-sitemap:before { content: @fa-var-sitemap; }
-.@{fa-css-prefix}-umbrella:before { content: @fa-var-umbrella; }
-.@{fa-css-prefix}-paste:before,
-.@{fa-css-prefix}-clipboard:before { content: @fa-var-clipboard; }
-.@{fa-css-prefix}-lightbulb-o:before { content: @fa-var-lightbulb-o; }
-.@{fa-css-prefix}-exchange:before { content: @fa-var-exchange; }
-.@{fa-css-prefix}-cloud-download:before { content: @fa-var-cloud-download; }
-.@{fa-css-prefix}-cloud-upload:before { content: @fa-var-cloud-upload; }
-.@{fa-css-prefix}-user-md:before { content: @fa-var-user-md; }
-.@{fa-css-prefix}-stethoscope:before { content: @fa-var-stethoscope; }
-.@{fa-css-prefix}-suitcase:before { content: @fa-var-suitcase; }
-.@{fa-css-prefix}-bell-o:before { content: @fa-var-bell-o; }
-.@{fa-css-prefix}-coffee:before { content: @fa-var-coffee; }
-.@{fa-css-prefix}-cutlery:before { content: @fa-var-cutlery; }
-.@{fa-css-prefix}-file-text-o:before { content: @fa-var-file-text-o; }
-.@{fa-css-prefix}-building-o:before { content: @fa-var-building-o; }
-.@{fa-css-prefix}-hospital-o:before { content: @fa-var-hospital-o; }
-.@{fa-css-prefix}-ambulance:before { content: @fa-var-ambulance; }
-.@{fa-css-prefix}-medkit:before { content: @fa-var-medkit; }
-.@{fa-css-prefix}-fighter-jet:before { content: @fa-var-fighter-jet; }
-.@{fa-css-prefix}-beer:before { content: @fa-var-beer; }
-.@{fa-css-prefix}-h-square:before { content: @fa-var-h-square; }
-.@{fa-css-prefix}-plus-square:before { content: @fa-var-plus-square; }
-.@{fa-css-prefix}-angle-double-left:before { content: @fa-var-angle-double-left; }
-.@{fa-css-prefix}-angle-double-right:before { content: @fa-var-angle-double-right; }
-.@{fa-css-prefix}-angle-double-up:before { content: @fa-var-angle-double-up; }
-.@{fa-css-prefix}-angle-double-down:before { content: @fa-var-angle-double-down; }
-.@{fa-css-prefix}-angle-left:before { content: @fa-var-angle-left; }
-.@{fa-css-prefix}-angle-right:before { content: @fa-var-angle-right; }
-.@{fa-css-prefix}-angle-up:before { content: @fa-var-angle-up; }
-.@{fa-css-prefix}-angle-down:before { content: @fa-var-angle-down; }
-.@{fa-css-prefix}-desktop:before { content: @fa-var-desktop; }
-.@{fa-css-prefix}-laptop:before { content: @fa-var-laptop; }
-.@{fa-css-prefix}-tablet:before { content: @fa-var-tablet; }
-.@{fa-css-prefix}-mobile-phone:before,
-.@{fa-css-prefix}-mobile:before { content: @fa-var-mobile; }
-.@{fa-css-prefix}-circle-o:before { content: @fa-var-circle-o; }
-.@{fa-css-prefix}-quote-left:before { content: @fa-var-quote-left; }
-.@{fa-css-prefix}-quote-right:before { content: @fa-var-quote-right; }
-.@{fa-css-prefix}-spinner:before { content: @fa-var-spinner; }
-.@{fa-css-prefix}-circle:before { content: @fa-var-circle; }
-.@{fa-css-prefix}-mail-reply:before,
-.@{fa-css-prefix}-reply:before { content: @fa-var-reply; }
-.@{fa-css-prefix}-github-alt:before { content: @fa-var-github-alt; }
-.@{fa-css-prefix}-folder-o:before { content: @fa-var-folder-o; }
-.@{fa-css-prefix}-folder-open-o:before { content: @fa-var-folder-open-o; }
-.@{fa-css-prefix}-smile-o:before { content: @fa-var-smile-o; }
-.@{fa-css-prefix}-frown-o:before { content: @fa-var-frown-o; }
-.@{fa-css-prefix}-meh-o:before { content: @fa-var-meh-o; }
-.@{fa-css-prefix}-gamepad:before { content: @fa-var-gamepad; }
-.@{fa-css-prefix}-keyboard-o:before { content: @fa-var-keyboard-o; }
-.@{fa-css-prefix}-flag-o:before { content: @fa-var-flag-o; }
-.@{fa-css-prefix}-flag-checkered:before { content: @fa-var-flag-checkered; }
-.@{fa-css-prefix}-terminal:before { content: @fa-var-terminal; }
-.@{fa-css-prefix}-code:before { content: @fa-var-code; }
-.@{fa-css-prefix}-mail-reply-all:before,
-.@{fa-css-prefix}-reply-all:before { content: @fa-var-reply-all; }
-.@{fa-css-prefix}-star-half-empty:before,
-.@{fa-css-prefix}-star-half-full:before,
-.@{fa-css-prefix}-star-half-o:before { content: @fa-var-star-half-o; }
-.@{fa-css-prefix}-location-arrow:before { content: @fa-var-location-arrow; }
-.@{fa-css-prefix}-crop:before { content: @fa-var-crop; }
-.@{fa-css-prefix}-code-fork:before { content: @fa-var-code-fork; }
-.@{fa-css-prefix}-unlink:before,
-.@{fa-css-prefix}-chain-broken:before { content: @fa-var-chain-broken; }
-.@{fa-css-prefix}-question:before { content: @fa-var-question; }
-.@{fa-css-prefix}-info:before { content: @fa-var-info; }
-.@{fa-css-prefix}-exclamation:before { content: @fa-var-exclamation; }
-.@{fa-css-prefix}-superscript:before { content: @fa-var-superscript; }
-.@{fa-css-prefix}-subscript:before { content: @fa-var-subscript; }
-.@{fa-css-prefix}-eraser:before { content: @fa-var-eraser; }
-.@{fa-css-prefix}-puzzle-piece:before { content: @fa-var-puzzle-piece; }
-.@{fa-css-prefix}-microphone:before { content: @fa-var-microphone; }
-.@{fa-css-prefix}-microphone-slash:before { content: @fa-var-microphone-slash; }
-.@{fa-css-prefix}-shield:before { content: @fa-var-shield; }
-.@{fa-css-prefix}-calendar-o:before { content: @fa-var-calendar-o; }
-.@{fa-css-prefix}-fire-extinguisher:before { content: @fa-var-fire-extinguisher; }
-.@{fa-css-prefix}-rocket:before { content: @fa-var-rocket; }
-.@{fa-css-prefix}-maxcdn:before { content: @fa-var-maxcdn; }
-.@{fa-css-prefix}-chevron-circle-left:before { content: @fa-var-chevron-circle-left; }
-.@{fa-css-prefix}-chevron-circle-right:before { content: @fa-var-chevron-circle-right; }
-.@{fa-css-prefix}-chevron-circle-up:before { content: @fa-var-chevron-circle-up; }
-.@{fa-css-prefix}-chevron-circle-down:before { content: @fa-var-chevron-circle-down; }
-.@{fa-css-prefix}-html5:before { content: @fa-var-html5; }
-.@{fa-css-prefix}-css3:before { content: @fa-var-css3; }
-.@{fa-css-prefix}-anchor:before { content: @fa-var-anchor; }
-.@{fa-css-prefix}-unlock-alt:before { content: @fa-var-unlock-alt; }
-.@{fa-css-prefix}-bullseye:before { content: @fa-var-bullseye; }
-.@{fa-css-prefix}-ellipsis-h:before { content: @fa-var-ellipsis-h; }
-.@{fa-css-prefix}-ellipsis-v:before { content: @fa-var-ellipsis-v; }
-.@{fa-css-prefix}-rss-square:before { content: @fa-var-rss-square; }
-.@{fa-css-prefix}-play-circle:before { content: @fa-var-play-circle; }
-.@{fa-css-prefix}-ticket:before { content: @fa-var-ticket; }
-.@{fa-css-prefix}-minus-square:before { content: @fa-var-minus-square; }
-.@{fa-css-prefix}-minus-square-o:before { content: @fa-var-minus-square-o; }
-.@{fa-css-prefix}-level-up:before { content: @fa-var-level-up; }
-.@{fa-css-prefix}-level-down:before { content: @fa-var-level-down; }
-.@{fa-css-prefix}-check-square:before { content: @fa-var-check-square; }
-.@{fa-css-prefix}-pencil-square:before { content: @fa-var-pencil-square; }
-.@{fa-css-prefix}-external-link-square:before { content: @fa-var-external-link-square; }
-.@{fa-css-prefix}-share-square:before { content: @fa-var-share-square; }
-.@{fa-css-prefix}-compass:before { content: @fa-var-compass; }
-.@{fa-css-prefix}-toggle-down:before,
-.@{fa-css-prefix}-caret-square-o-down:before { content: @fa-var-caret-square-o-down; }
-.@{fa-css-prefix}-toggle-up:before,
-.@{fa-css-prefix}-caret-square-o-up:before { content: @fa-var-caret-square-o-up; }
-.@{fa-css-prefix}-toggle-right:before,
-.@{fa-css-prefix}-caret-square-o-right:before { content: @fa-var-caret-square-o-right; }
-.@{fa-css-prefix}-euro:before,
-.@{fa-css-prefix}-eur:before { content: @fa-var-eur; }
-.@{fa-css-prefix}-gbp:before { content: @fa-var-gbp; }
-.@{fa-css-prefix}-dollar:before,
-.@{fa-css-prefix}-usd:before { content: @fa-var-usd; }
-.@{fa-css-prefix}-rupee:before,
-.@{fa-css-prefix}-inr:before { content: @fa-var-inr; }
-.@{fa-css-prefix}-cny:before,
-.@{fa-css-prefix}-rmb:before,
-.@{fa-css-prefix}-yen:before,
-.@{fa-css-prefix}-jpy:before { content: @fa-var-jpy; }
-.@{fa-css-prefix}-ruble:before,
-.@{fa-css-prefix}-rouble:before,
-.@{fa-css-prefix}-rub:before { content: @fa-var-rub; }
-.@{fa-css-prefix}-won:before,
-.@{fa-css-prefix}-krw:before { content: @fa-var-krw; }
-.@{fa-css-prefix}-bitcoin:before,
-.@{fa-css-prefix}-btc:before { content: @fa-var-btc; }
-.@{fa-css-prefix}-file:before { content: @fa-var-file; }
-.@{fa-css-prefix}-file-text:before { content: @fa-var-file-text; }
-.@{fa-css-prefix}-sort-alpha-asc:before { content: @fa-var-sort-alpha-asc; }
-.@{fa-css-prefix}-sort-alpha-desc:before { content: @fa-var-sort-alpha-desc; }
-.@{fa-css-prefix}-sort-amount-asc:before { content: @fa-var-sort-amount-asc; }
-.@{fa-css-prefix}-sort-amount-desc:before { content: @fa-var-sort-amount-desc; }
-.@{fa-css-prefix}-sort-numeric-asc:before { content: @fa-var-sort-numeric-asc; }
-.@{fa-css-prefix}-sort-numeric-desc:before { content: @fa-var-sort-numeric-desc; }
-.@{fa-css-prefix}-thumbs-up:before { content: @fa-var-thumbs-up; }
-.@{fa-css-prefix}-thumbs-down:before { content: @fa-var-thumbs-down; }
-.@{fa-css-prefix}-youtube-square:before { content: @fa-var-youtube-square; }
-.@{fa-css-prefix}-youtube:before { content: @fa-var-youtube; }
-.@{fa-css-prefix}-xing:before { content: @fa-var-xing; }
-.@{fa-css-prefix}-xing-square:before { content: @fa-var-xing-square; }
-.@{fa-css-prefix}-youtube-play:before { content: @fa-var-youtube-play; }
-.@{fa-css-prefix}-dropbox:before { content: @fa-var-dropbox; }
-.@{fa-css-prefix}-stack-overflow:before { content: @fa-var-stack-overflow; }
-.@{fa-css-prefix}-instagram:before { content: @fa-var-instagram; }
-.@{fa-css-prefix}-flickr:before { content: @fa-var-flickr; }
-.@{fa-css-prefix}-adn:before { content: @fa-var-adn; }
-.@{fa-css-prefix}-bitbucket:before { content: @fa-var-bitbucket; }
-.@{fa-css-prefix}-bitbucket-square:before { content: @fa-var-bitbucket-square; }
-.@{fa-css-prefix}-tumblr:before { content: @fa-var-tumblr; }
-.@{fa-css-prefix}-tumblr-square:before { content: @fa-var-tumblr-square; }
-.@{fa-css-prefix}-long-arrow-down:before { content: @fa-var-long-arrow-down; }
-.@{fa-css-prefix}-long-arrow-up:before { content: @fa-var-long-arrow-up; }
-.@{fa-css-prefix}-long-arrow-left:before { content: @fa-var-long-arrow-left; }
-.@{fa-css-prefix}-long-arrow-right:before { content: @fa-var-long-arrow-right; }
-.@{fa-css-prefix}-apple:before { content: @fa-var-apple; }
-.@{fa-css-prefix}-windows:before { content: @fa-var-windows; }
-.@{fa-css-prefix}-android:before { content: @fa-var-android; }
-.@{fa-css-prefix}-linux:before { content: @fa-var-linux; }
-.@{fa-css-prefix}-dribbble:before { content: @fa-var-dribbble; }
-.@{fa-css-prefix}-skype:before { content: @fa-var-skype; }
-.@{fa-css-prefix}-foursquare:before { content: @fa-var-foursquare; }
-.@{fa-css-prefix}-trello:before { content: @fa-var-trello; }
-.@{fa-css-prefix}-female:before { content: @fa-var-female; }
-.@{fa-css-prefix}-male:before { content: @fa-var-male; }
-.@{fa-css-prefix}-gittip:before,
-.@{fa-css-prefix}-gratipay:before { content: @fa-var-gratipay; }
-.@{fa-css-prefix}-sun-o:before { content: @fa-var-sun-o; }
-.@{fa-css-prefix}-moon-o:before { content: @fa-var-moon-o; }
-.@{fa-css-prefix}-archive:before { content: @fa-var-archive; }
-.@{fa-css-prefix}-bug:before { content: @fa-var-bug; }
-.@{fa-css-prefix}-vk:before { content: @fa-var-vk; }
-.@{fa-css-prefix}-weibo:before { content: @fa-var-weibo; }
-.@{fa-css-prefix}-renren:before { content: @fa-var-renren; }
-.@{fa-css-prefix}-pagelines:before { content: @fa-var-pagelines; }
-.@{fa-css-prefix}-stack-exchange:before { content: @fa-var-stack-exchange; }
-.@{fa-css-prefix}-arrow-circle-o-right:before { content: @fa-var-arrow-circle-o-right; }
-.@{fa-css-prefix}-arrow-circle-o-left:before { content: @fa-var-arrow-circle-o-left; }
-.@{fa-css-prefix}-toggle-left:before,
-.@{fa-css-prefix}-caret-square-o-left:before { content: @fa-var-caret-square-o-left; }
-.@{fa-css-prefix}-dot-circle-o:before { content: @fa-var-dot-circle-o; }
-.@{fa-css-prefix}-wheelchair:before { content: @fa-var-wheelchair; }
-.@{fa-css-prefix}-vimeo-square:before { content: @fa-var-vimeo-square; }
-.@{fa-css-prefix}-turkish-lira:before,
-.@{fa-css-prefix}-try:before { content: @fa-var-try; }
-.@{fa-css-prefix}-plus-square-o:before { content: @fa-var-plus-square-o; }
-.@{fa-css-prefix}-space-shuttle:before { content: @fa-var-space-shuttle; }
-.@{fa-css-prefix}-slack:before { content: @fa-var-slack; }
-.@{fa-css-prefix}-envelope-square:before { content: @fa-var-envelope-square; }
-.@{fa-css-prefix}-wordpress:before { content: @fa-var-wordpress; }
-.@{fa-css-prefix}-openid:before { content: @fa-var-openid; }
-.@{fa-css-prefix}-institution:before,
-.@{fa-css-prefix}-bank:before,
-.@{fa-css-prefix}-university:before { content: @fa-var-university; }
-.@{fa-css-prefix}-mortar-board:before,
-.@{fa-css-prefix}-graduation-cap:before { content: @fa-var-graduation-cap; }
-.@{fa-css-prefix}-yahoo:before { content: @fa-var-yahoo; }
-.@{fa-css-prefix}-google:before { content: @fa-var-google; }
-.@{fa-css-prefix}-reddit:before { content: @fa-var-reddit; }
-.@{fa-css-prefix}-reddit-square:before { content: @fa-var-reddit-square; }
-.@{fa-css-prefix}-stumbleupon-circle:before { content: @fa-var-stumbleupon-circle; }
-.@{fa-css-prefix}-stumbleupon:before { content: @fa-var-stumbleupon; }
-.@{fa-css-prefix}-delicious:before { content: @fa-var-delicious; }
-.@{fa-css-prefix}-digg:before { content: @fa-var-digg; }
-.@{fa-css-prefix}-pied-piper:before { content: @fa-var-pied-piper; }
-.@{fa-css-prefix}-pied-piper-alt:before { content: @fa-var-pied-piper-alt; }
-.@{fa-css-prefix}-drupal:before { content: @fa-var-drupal; }
-.@{fa-css-prefix}-joomla:before { content: @fa-var-joomla; }
-.@{fa-css-prefix}-language:before { content: @fa-var-language; }
-.@{fa-css-prefix}-fax:before { content: @fa-var-fax; }
-.@{fa-css-prefix}-building:before { content: @fa-var-building; }
-.@{fa-css-prefix}-child:before { content: @fa-var-child; }
-.@{fa-css-prefix}-paw:before { content: @fa-var-paw; }
-.@{fa-css-prefix}-spoon:before { content: @fa-var-spoon; }
-.@{fa-css-prefix}-cube:before { content: @fa-var-cube; }
-.@{fa-css-prefix}-cubes:before { content: @fa-var-cubes; }
-.@{fa-css-prefix}-behance:before { content: @fa-var-behance; }
-.@{fa-css-prefix}-behance-square:before { content: @fa-var-behance-square; }
-.@{fa-css-prefix}-steam:before { content: @fa-var-steam; }
-.@{fa-css-prefix}-steam-square:before { content: @fa-var-steam-square; }
-.@{fa-css-prefix}-recycle:before { content: @fa-var-recycle; }
-.@{fa-css-prefix}-automobile:before,
-.@{fa-css-prefix}-car:before { content: @fa-var-car; }
-.@{fa-css-prefix}-cab:before,
-.@{fa-css-prefix}-taxi:before { content: @fa-var-taxi; }
-.@{fa-css-prefix}-tree:before { content: @fa-var-tree; }
-.@{fa-css-prefix}-spotify:before { content: @fa-var-spotify; }
-.@{fa-css-prefix}-deviantart:before { content: @fa-var-deviantart; }
-.@{fa-css-prefix}-soundcloud:before { content: @fa-var-soundcloud; }
-.@{fa-css-prefix}-database:before { content: @fa-var-database; }
-.@{fa-css-prefix}-file-pdf-o:before { content: @fa-var-file-pdf-o; }
-.@{fa-css-prefix}-file-word-o:before { content: @fa-var-file-word-o; }
-.@{fa-css-prefix}-file-excel-o:before { content: @fa-var-file-excel-o; }
-.@{fa-css-prefix}-file-powerpoint-o:before { content: @fa-var-file-powerpoint-o; }
-.@{fa-css-prefix}-file-photo-o:before,
-.@{fa-css-prefix}-file-picture-o:before,
-.@{fa-css-prefix}-file-image-o:before { content: @fa-var-file-image-o; }
-.@{fa-css-prefix}-file-zip-o:before,
-.@{fa-css-prefix}-file-archive-o:before { content: @fa-var-file-archive-o; }
-.@{fa-css-prefix}-file-sound-o:before,
-.@{fa-css-prefix}-file-audio-o:before { content: @fa-var-file-audio-o; }
-.@{fa-css-prefix}-file-movie-o:before,
-.@{fa-css-prefix}-file-video-o:before { content: @fa-var-file-video-o; }
-.@{fa-css-prefix}-file-code-o:before { content: @fa-var-file-code-o; }
-.@{fa-css-prefix}-vine:before { content: @fa-var-vine; }
-.@{fa-css-prefix}-codepen:before { content: @fa-var-codepen; }
-.@{fa-css-prefix}-jsfiddle:before { content: @fa-var-jsfiddle; }
-.@{fa-css-prefix}-life-bouy:before,
-.@{fa-css-prefix}-life-buoy:before,
-.@{fa-css-prefix}-life-saver:before,
-.@{fa-css-prefix}-support:before,
-.@{fa-css-prefix}-life-ring:before { content: @fa-var-life-ring; }
-.@{fa-css-prefix}-circle-o-notch:before { content: @fa-var-circle-o-notch; }
-.@{fa-css-prefix}-ra:before,
-.@{fa-css-prefix}-rebel:before { content: @fa-var-rebel; }
-.@{fa-css-prefix}-ge:before,
-.@{fa-css-prefix}-empire:before { content: @fa-var-empire; }
-.@{fa-css-prefix}-git-square:before { content: @fa-var-git-square; }
-.@{fa-css-prefix}-git:before { content: @fa-var-git; }
-.@{fa-css-prefix}-hacker-news:before { content: @fa-var-hacker-news; }
-.@{fa-css-prefix}-tencent-weibo:before { content: @fa-var-tencent-weibo; }
-.@{fa-css-prefix}-qq:before { content: @fa-var-qq; }
-.@{fa-css-prefix}-wechat:before,
-.@{fa-css-prefix}-weixin:before { content: @fa-var-weixin; }
-.@{fa-css-prefix}-send:before,
-.@{fa-css-prefix}-paper-plane:before { content: @fa-var-paper-plane; }
-.@{fa-css-prefix}-send-o:before,
-.@{fa-css-prefix}-paper-plane-o:before { content: @fa-var-paper-plane-o; }
-.@{fa-css-prefix}-history:before { content: @fa-var-history; }
-.@{fa-css-prefix}-genderless:before,
-.@{fa-css-prefix}-circle-thin:before { content: @fa-var-circle-thin; }
-.@{fa-css-prefix}-header:before { content: @fa-var-header; }
-.@{fa-css-prefix}-paragraph:before { content: @fa-var-paragraph; }
-.@{fa-css-prefix}-sliders:before { content: @fa-var-sliders; }
-.@{fa-css-prefix}-share-alt:before { content: @fa-var-share-alt; }
-.@{fa-css-prefix}-share-alt-square:before { content: @fa-var-share-alt-square; }
-.@{fa-css-prefix}-bomb:before { content: @fa-var-bomb; }
-.@{fa-css-prefix}-soccer-ball-o:before,
-.@{fa-css-prefix}-futbol-o:before { content: @fa-var-futbol-o; }
-.@{fa-css-prefix}-tty:before { content: @fa-var-tty; }
-.@{fa-css-prefix}-binoculars:before { content: @fa-var-binoculars; }
-.@{fa-css-prefix}-plug:before { content: @fa-var-plug; }
-.@{fa-css-prefix}-slideshare:before { content: @fa-var-slideshare; }
-.@{fa-css-prefix}-twitch:before { content: @fa-var-twitch; }
-.@{fa-css-prefix}-yelp:before { content: @fa-var-yelp; }
-.@{fa-css-prefix}-newspaper-o:before { content: @fa-var-newspaper-o; }
-.@{fa-css-prefix}-wifi:before { content: @fa-var-wifi; }
-.@{fa-css-prefix}-calculator:before { content: @fa-var-calculator; }
-.@{fa-css-prefix}-paypal:before { content: @fa-var-paypal; }
-.@{fa-css-prefix}-google-wallet:before { content: @fa-var-google-wallet; }
-.@{fa-css-prefix}-cc-visa:before { content: @fa-var-cc-visa; }
-.@{fa-css-prefix}-cc-mastercard:before { content: @fa-var-cc-mastercard; }
-.@{fa-css-prefix}-cc-discover:before { content: @fa-var-cc-discover; }
-.@{fa-css-prefix}-cc-amex:before { content: @fa-var-cc-amex; }
-.@{fa-css-prefix}-cc-paypal:before { content: @fa-var-cc-paypal; }
-.@{fa-css-prefix}-cc-stripe:before { content: @fa-var-cc-stripe; }
-.@{fa-css-prefix}-bell-slash:before { content: @fa-var-bell-slash; }
-.@{fa-css-prefix}-bell-slash-o:before { content: @fa-var-bell-slash-o; }
-.@{fa-css-prefix}-trash:before { content: @fa-var-trash; }
-.@{fa-css-prefix}-copyright:before { content: @fa-var-copyright; }
-.@{fa-css-prefix}-at:before { content: @fa-var-at; }
-.@{fa-css-prefix}-eyedropper:before { content: @fa-var-eyedropper; }
-.@{fa-css-prefix}-paint-brush:before { content: @fa-var-paint-brush; }
-.@{fa-css-prefix}-birthday-cake:before { content: @fa-var-birthday-cake; }
-.@{fa-css-prefix}-area-chart:before { content: @fa-var-area-chart; }
-.@{fa-css-prefix}-pie-chart:before { content: @fa-var-pie-chart; }
-.@{fa-css-prefix}-line-chart:before { content: @fa-var-line-chart; }
-.@{fa-css-prefix}-lastfm:before { content: @fa-var-lastfm; }
-.@{fa-css-prefix}-lastfm-square:before { content: @fa-var-lastfm-square; }
-.@{fa-css-prefix}-toggle-off:before { content: @fa-var-toggle-off; }
-.@{fa-css-prefix}-toggle-on:before { content: @fa-var-toggle-on; }
-.@{fa-css-prefix}-bicycle:before { content: @fa-var-bicycle; }
-.@{fa-css-prefix}-bus:before { content: @fa-var-bus; }
-.@{fa-css-prefix}-ioxhost:before { content: @fa-var-ioxhost; }
-.@{fa-css-prefix}-angellist:before { content: @fa-var-angellist; }
-.@{fa-css-prefix}-cc:before { content: @fa-var-cc; }
-.@{fa-css-prefix}-shekel:before,
-.@{fa-css-prefix}-sheqel:before,
-.@{fa-css-prefix}-ils:before { content: @fa-var-ils; }
-.@{fa-css-prefix}-meanpath:before { content: @fa-var-meanpath; }
-.@{fa-css-prefix}-buysellads:before { content: @fa-var-buysellads; }
-.@{fa-css-prefix}-connectdevelop:before { content: @fa-var-connectdevelop; }
-.@{fa-css-prefix}-dashcube:before { content: @fa-var-dashcube; }
-.@{fa-css-prefix}-forumbee:before { content: @fa-var-forumbee; }
-.@{fa-css-prefix}-leanpub:before { content: @fa-var-leanpub; }
-.@{fa-css-prefix}-sellsy:before { content: @fa-var-sellsy; }
-.@{fa-css-prefix}-shirtsinbulk:before { content: @fa-var-shirtsinbulk; }
-.@{fa-css-prefix}-simplybuilt:before { content: @fa-var-simplybuilt; }
-.@{fa-css-prefix}-skyatlas:before { content: @fa-var-skyatlas; }
-.@{fa-css-prefix}-cart-plus:before { content: @fa-var-cart-plus; }
-.@{fa-css-prefix}-cart-arrow-down:before { content: @fa-var-cart-arrow-down; }
-.@{fa-css-prefix}-diamond:before { content: @fa-var-diamond; }
-.@{fa-css-prefix}-ship:before { content: @fa-var-ship; }
-.@{fa-css-prefix}-user-secret:before { content: @fa-var-user-secret; }
-.@{fa-css-prefix}-motorcycle:before { content: @fa-var-motorcycle; }
-.@{fa-css-prefix}-street-view:before { content: @fa-var-street-view; }
-.@{fa-css-prefix}-heartbeat:before { content: @fa-var-heartbeat; }
-.@{fa-css-prefix}-venus:before { content: @fa-var-venus; }
-.@{fa-css-prefix}-mars:before { content: @fa-var-mars; }
-.@{fa-css-prefix}-mercury:before { content: @fa-var-mercury; }
-.@{fa-css-prefix}-transgender:before { content: @fa-var-transgender; }
-.@{fa-css-prefix}-transgender-alt:before { content: @fa-var-transgender-alt; }
-.@{fa-css-prefix}-venus-double:before { content: @fa-var-venus-double; }
-.@{fa-css-prefix}-mars-double:before { content: @fa-var-mars-double; }
-.@{fa-css-prefix}-venus-mars:before { content: @fa-var-venus-mars; }
-.@{fa-css-prefix}-mars-stroke:before { content: @fa-var-mars-stroke; }
-.@{fa-css-prefix}-mars-stroke-v:before { content: @fa-var-mars-stroke-v; }
-.@{fa-css-prefix}-mars-stroke-h:before { content: @fa-var-mars-stroke-h; }
-.@{fa-css-prefix}-neuter:before { content: @fa-var-neuter; }
-.@{fa-css-prefix}-facebook-official:before { content: @fa-var-facebook-official; }
-.@{fa-css-prefix}-pinterest-p:before { content: @fa-var-pinterest-p; }
-.@{fa-css-prefix}-whatsapp:before { content: @fa-var-whatsapp; }
-.@{fa-css-prefix}-server:before { content: @fa-var-server; }
-.@{fa-css-prefix}-user-plus:before { content: @fa-var-user-plus; }
-.@{fa-css-prefix}-user-times:before { content: @fa-var-user-times; }
-.@{fa-css-prefix}-hotel:before,
-.@{fa-css-prefix}-bed:before { content: @fa-var-bed; }
-.@{fa-css-prefix}-viacoin:before { content: @fa-var-viacoin; }
-.@{fa-css-prefix}-train:before { content: @fa-var-train; }
-.@{fa-css-prefix}-subway:before { content: @fa-var-subway; }
-.@{fa-css-prefix}-medium:before { content: @fa-var-medium; }
diff --git a/presentation/contrib/font-awesome-4.3.0/less/larger.less b/presentation/contrib/font-awesome-4.3.0/less/larger.less
deleted file mode 100644
index c9d646770..000000000
--- a/presentation/contrib/font-awesome-4.3.0/less/larger.less
+++ /dev/null
@@ -1,13 +0,0 @@
-// Icon Sizes
-// -------------------------
-
-/* makes the font 33% larger relative to the icon container */
-.@{fa-css-prefix}-lg {
- font-size: (4em / 3);
- line-height: (3em / 4);
- vertical-align: -15%;
-}
-.@{fa-css-prefix}-2x { font-size: 2em; }
-.@{fa-css-prefix}-3x { font-size: 3em; }
-.@{fa-css-prefix}-4x { font-size: 4em; }
-.@{fa-css-prefix}-5x { font-size: 5em; }
diff --git a/presentation/contrib/font-awesome-4.3.0/less/list.less b/presentation/contrib/font-awesome-4.3.0/less/list.less
deleted file mode 100644
index 0b440382f..000000000
--- a/presentation/contrib/font-awesome-4.3.0/less/list.less
+++ /dev/null
@@ -1,19 +0,0 @@
-// List Icons
-// -------------------------
-
-.@{fa-css-prefix}-ul {
- padding-left: 0;
- margin-left: @fa-li-width;
- list-style-type: none;
- > li { position: relative; }
-}
-.@{fa-css-prefix}-li {
- position: absolute;
- left: -@fa-li-width;
- width: @fa-li-width;
- top: (2em / 14);
- text-align: center;
- &.@{fa-css-prefix}-lg {
- left: (-@fa-li-width + (4em / 14));
- }
-}
diff --git a/presentation/contrib/font-awesome-4.3.0/less/mixins.less b/presentation/contrib/font-awesome-4.3.0/less/mixins.less
deleted file mode 100644
index c97f4604c..000000000
--- a/presentation/contrib/font-awesome-4.3.0/less/mixins.less
+++ /dev/null
@@ -1,27 +0,0 @@
-// Mixins
-// --------------------------
-
-.fa-icon() {
- display: inline-block;
- font: normal normal normal @fa-font-size-base/1 FontAwesome; // shortening font declaration
- font-size: inherit; // can't have font-size inherit on line above, so need to override
- text-rendering: auto; // optimizelegibility throws things off #1094
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- transform: translate(0, 0); // ensures no half-pixel rendering in firefox
-
-}
-
-.fa-icon-rotate(@degrees, @rotation) {
- filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation);
- -webkit-transform: rotate(@degrees);
- -ms-transform: rotate(@degrees);
- transform: rotate(@degrees);
-}
-
-.fa-icon-flip(@horiz, @vert, @rotation) {
- filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation, mirror=1);
- -webkit-transform: scale(@horiz, @vert);
- -ms-transform: scale(@horiz, @vert);
- transform: scale(@horiz, @vert);
-}
diff --git a/presentation/contrib/font-awesome-4.3.0/less/path.less b/presentation/contrib/font-awesome-4.3.0/less/path.less
deleted file mode 100644
index 9211e6659..000000000
--- a/presentation/contrib/font-awesome-4.3.0/less/path.less
+++ /dev/null
@@ -1,15 +0,0 @@
-/* FONT PATH
- * -------------------------- */
-
-@font-face {
- font-family: 'FontAwesome';
- src: url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}');
- src: url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype'),
- url('@{fa-font-path}/fontawesome-webfont.woff2?v=@{fa-version}') format('woff2'),
- url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'),
- url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'),
- url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg');
-// src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
- font-weight: normal;
- font-style: normal;
-}
diff --git a/presentation/contrib/font-awesome-4.3.0/less/rotated-flipped.less b/presentation/contrib/font-awesome-4.3.0/less/rotated-flipped.less
deleted file mode 100644
index f6ba81475..000000000
--- a/presentation/contrib/font-awesome-4.3.0/less/rotated-flipped.less
+++ /dev/null
@@ -1,20 +0,0 @@
-// Rotated & Flipped Icons
-// -------------------------
-
-.@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); }
-.@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); }
-.@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); }
-
-.@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); }
-.@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); }
-
-// Hook for IE8-9
-// -------------------------
-
-:root .@{fa-css-prefix}-rotate-90,
-:root .@{fa-css-prefix}-rotate-180,
-:root .@{fa-css-prefix}-rotate-270,
-:root .@{fa-css-prefix}-flip-horizontal,
-:root .@{fa-css-prefix}-flip-vertical {
- filter: none;
-}
diff --git a/presentation/contrib/font-awesome-4.3.0/less/stacked.less b/presentation/contrib/font-awesome-4.3.0/less/stacked.less
deleted file mode 100644
index fc53fb0e7..000000000
--- a/presentation/contrib/font-awesome-4.3.0/less/stacked.less
+++ /dev/null
@@ -1,20 +0,0 @@
-// Stacked Icons
-// -------------------------
-
-.@{fa-css-prefix}-stack {
- position: relative;
- display: inline-block;
- width: 2em;
- height: 2em;
- line-height: 2em;
- vertical-align: middle;
-}
-.@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x {
- position: absolute;
- left: 0;
- width: 100%;
- text-align: center;
-}
-.@{fa-css-prefix}-stack-1x { line-height: inherit; }
-.@{fa-css-prefix}-stack-2x { font-size: 2em; }
-.@{fa-css-prefix}-inverse { color: @fa-inverse; }
diff --git a/presentation/contrib/font-awesome-4.3.0/less/variables.less b/presentation/contrib/font-awesome-4.3.0/less/variables.less
deleted file mode 100644
index d526064c8..000000000
--- a/presentation/contrib/font-awesome-4.3.0/less/variables.less
+++ /dev/null
@@ -1,606 +0,0 @@
-// Variables
-// --------------------------
-
-@fa-font-path: "../fonts";
-@fa-font-size-base: 14px;
-//@fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.3.0/fonts"; // for referencing Bootstrap CDN font files directly
-@fa-css-prefix: fa;
-@fa-version: "4.3.0";
-@fa-border-color: #eee;
-@fa-inverse: #fff;
-@fa-li-width: (30em / 14);
-
-@fa-var-adjust: "\f042";
-@fa-var-adn: "\f170";
-@fa-var-align-center: "\f037";
-@fa-var-align-justify: "\f039";
-@fa-var-align-left: "\f036";
-@fa-var-align-right: "\f038";
-@fa-var-ambulance: "\f0f9";
-@fa-var-anchor: "\f13d";
-@fa-var-android: "\f17b";
-@fa-var-angellist: "\f209";
-@fa-var-angle-double-down: "\f103";
-@fa-var-angle-double-left: "\f100";
-@fa-var-angle-double-right: "\f101";
-@fa-var-angle-double-up: "\f102";
-@fa-var-angle-down: "\f107";
-@fa-var-angle-left: "\f104";
-@fa-var-angle-right: "\f105";
-@fa-var-angle-up: "\f106";
-@fa-var-apple: "\f179";
-@fa-var-archive: "\f187";
-@fa-var-area-chart: "\f1fe";
-@fa-var-arrow-circle-down: "\f0ab";
-@fa-var-arrow-circle-left: "\f0a8";
-@fa-var-arrow-circle-o-down: "\f01a";
-@fa-var-arrow-circle-o-left: "\f190";
-@fa-var-arrow-circle-o-right: "\f18e";
-@fa-var-arrow-circle-o-up: "\f01b";
-@fa-var-arrow-circle-right: "\f0a9";
-@fa-var-arrow-circle-up: "\f0aa";
-@fa-var-arrow-down: "\f063";
-@fa-var-arrow-left: "\f060";
-@fa-var-arrow-right: "\f061";
-@fa-var-arrow-up: "\f062";
-@fa-var-arrows: "\f047";
-@fa-var-arrows-alt: "\f0b2";
-@fa-var-arrows-h: "\f07e";
-@fa-var-arrows-v: "\f07d";
-@fa-var-asterisk: "\f069";
-@fa-var-at: "\f1fa";
-@fa-var-automobile: "\f1b9";
-@fa-var-backward: "\f04a";
-@fa-var-ban: "\f05e";
-@fa-var-bank: "\f19c";
-@fa-var-bar-chart: "\f080";
-@fa-var-bar-chart-o: "\f080";
-@fa-var-barcode: "\f02a";
-@fa-var-bars: "\f0c9";
-@fa-var-bed: "\f236";
-@fa-var-beer: "\f0fc";
-@fa-var-behance: "\f1b4";
-@fa-var-behance-square: "\f1b5";
-@fa-var-bell: "\f0f3";
-@fa-var-bell-o: "\f0a2";
-@fa-var-bell-slash: "\f1f6";
-@fa-var-bell-slash-o: "\f1f7";
-@fa-var-bicycle: "\f206";
-@fa-var-binoculars: "\f1e5";
-@fa-var-birthday-cake: "\f1fd";
-@fa-var-bitbucket: "\f171";
-@fa-var-bitbucket-square: "\f172";
-@fa-var-bitcoin: "\f15a";
-@fa-var-bold: "\f032";
-@fa-var-bolt: "\f0e7";
-@fa-var-bomb: "\f1e2";
-@fa-var-book: "\f02d";
-@fa-var-bookmark: "\f02e";
-@fa-var-bookmark-o: "\f097";
-@fa-var-briefcase: "\f0b1";
-@fa-var-btc: "\f15a";
-@fa-var-bug: "\f188";
-@fa-var-building: "\f1ad";
-@fa-var-building-o: "\f0f7";
-@fa-var-bullhorn: "\f0a1";
-@fa-var-bullseye: "\f140";
-@fa-var-bus: "\f207";
-@fa-var-buysellads: "\f20d";
-@fa-var-cab: "\f1ba";
-@fa-var-calculator: "\f1ec";
-@fa-var-calendar: "\f073";
-@fa-var-calendar-o: "\f133";
-@fa-var-camera: "\f030";
-@fa-var-camera-retro: "\f083";
-@fa-var-car: "\f1b9";
-@fa-var-caret-down: "\f0d7";
-@fa-var-caret-left: "\f0d9";
-@fa-var-caret-right: "\f0da";
-@fa-var-caret-square-o-down: "\f150";
-@fa-var-caret-square-o-left: "\f191";
-@fa-var-caret-square-o-right: "\f152";
-@fa-var-caret-square-o-up: "\f151";
-@fa-var-caret-up: "\f0d8";
-@fa-var-cart-arrow-down: "\f218";
-@fa-var-cart-plus: "\f217";
-@fa-var-cc: "\f20a";
-@fa-var-cc-amex: "\f1f3";
-@fa-var-cc-discover: "\f1f2";
-@fa-var-cc-mastercard: "\f1f1";
-@fa-var-cc-paypal: "\f1f4";
-@fa-var-cc-stripe: "\f1f5";
-@fa-var-cc-visa: "\f1f0";
-@fa-var-certificate: "\f0a3";
-@fa-var-chain: "\f0c1";
-@fa-var-chain-broken: "\f127";
-@fa-var-check: "\f00c";
-@fa-var-check-circle: "\f058";
-@fa-var-check-circle-o: "\f05d";
-@fa-var-check-square: "\f14a";
-@fa-var-check-square-o: "\f046";
-@fa-var-chevron-circle-down: "\f13a";
-@fa-var-chevron-circle-left: "\f137";
-@fa-var-chevron-circle-right: "\f138";
-@fa-var-chevron-circle-up: "\f139";
-@fa-var-chevron-down: "\f078";
-@fa-var-chevron-left: "\f053";
-@fa-var-chevron-right: "\f054";
-@fa-var-chevron-up: "\f077";
-@fa-var-child: "\f1ae";
-@fa-var-circle: "\f111";
-@fa-var-circle-o: "\f10c";
-@fa-var-circle-o-notch: "\f1ce";
-@fa-var-circle-thin: "\f1db";
-@fa-var-clipboard: "\f0ea";
-@fa-var-clock-o: "\f017";
-@fa-var-close: "\f00d";
-@fa-var-cloud: "\f0c2";
-@fa-var-cloud-download: "\f0ed";
-@fa-var-cloud-upload: "\f0ee";
-@fa-var-cny: "\f157";
-@fa-var-code: "\f121";
-@fa-var-code-fork: "\f126";
-@fa-var-codepen: "\f1cb";
-@fa-var-coffee: "\f0f4";
-@fa-var-cog: "\f013";
-@fa-var-cogs: "\f085";
-@fa-var-columns: "\f0db";
-@fa-var-comment: "\f075";
-@fa-var-comment-o: "\f0e5";
-@fa-var-comments: "\f086";
-@fa-var-comments-o: "\f0e6";
-@fa-var-compass: "\f14e";
-@fa-var-compress: "\f066";
-@fa-var-connectdevelop: "\f20e";
-@fa-var-copy: "\f0c5";
-@fa-var-copyright: "\f1f9";
-@fa-var-credit-card: "\f09d";
-@fa-var-crop: "\f125";
-@fa-var-crosshairs: "\f05b";
-@fa-var-css3: "\f13c";
-@fa-var-cube: "\f1b2";
-@fa-var-cubes: "\f1b3";
-@fa-var-cut: "\f0c4";
-@fa-var-cutlery: "\f0f5";
-@fa-var-dashboard: "\f0e4";
-@fa-var-dashcube: "\f210";
-@fa-var-database: "\f1c0";
-@fa-var-dedent: "\f03b";
-@fa-var-delicious: "\f1a5";
-@fa-var-desktop: "\f108";
-@fa-var-deviantart: "\f1bd";
-@fa-var-diamond: "\f219";
-@fa-var-digg: "\f1a6";
-@fa-var-dollar: "\f155";
-@fa-var-dot-circle-o: "\f192";
-@fa-var-download: "\f019";
-@fa-var-dribbble: "\f17d";
-@fa-var-dropbox: "\f16b";
-@fa-var-drupal: "\f1a9";
-@fa-var-edit: "\f044";
-@fa-var-eject: "\f052";
-@fa-var-ellipsis-h: "\f141";
-@fa-var-ellipsis-v: "\f142";
-@fa-var-empire: "\f1d1";
-@fa-var-envelope: "\f0e0";
-@fa-var-envelope-o: "\f003";
-@fa-var-envelope-square: "\f199";
-@fa-var-eraser: "\f12d";
-@fa-var-eur: "\f153";
-@fa-var-euro: "\f153";
-@fa-var-exchange: "\f0ec";
-@fa-var-exclamation: "\f12a";
-@fa-var-exclamation-circle: "\f06a";
-@fa-var-exclamation-triangle: "\f071";
-@fa-var-expand: "\f065";
-@fa-var-external-link: "\f08e";
-@fa-var-external-link-square: "\f14c";
-@fa-var-eye: "\f06e";
-@fa-var-eye-slash: "\f070";
-@fa-var-eyedropper: "\f1fb";
-@fa-var-facebook: "\f09a";
-@fa-var-facebook-f: "\f09a";
-@fa-var-facebook-official: "\f230";
-@fa-var-facebook-square: "\f082";
-@fa-var-fast-backward: "\f049";
-@fa-var-fast-forward: "\f050";
-@fa-var-fax: "\f1ac";
-@fa-var-female: "\f182";
-@fa-var-fighter-jet: "\f0fb";
-@fa-var-file: "\f15b";
-@fa-var-file-archive-o: "\f1c6";
-@fa-var-file-audio-o: "\f1c7";
-@fa-var-file-code-o: "\f1c9";
-@fa-var-file-excel-o: "\f1c3";
-@fa-var-file-image-o: "\f1c5";
-@fa-var-file-movie-o: "\f1c8";
-@fa-var-file-o: "\f016";
-@fa-var-file-pdf-o: "\f1c1";
-@fa-var-file-photo-o: "\f1c5";
-@fa-var-file-picture-o: "\f1c5";
-@fa-var-file-powerpoint-o: "\f1c4";
-@fa-var-file-sound-o: "\f1c7";
-@fa-var-file-text: "\f15c";
-@fa-var-file-text-o: "\f0f6";
-@fa-var-file-video-o: "\f1c8";
-@fa-var-file-word-o: "\f1c2";
-@fa-var-file-zip-o: "\f1c6";
-@fa-var-files-o: "\f0c5";
-@fa-var-film: "\f008";
-@fa-var-filter: "\f0b0";
-@fa-var-fire: "\f06d";
-@fa-var-fire-extinguisher: "\f134";
-@fa-var-flag: "\f024";
-@fa-var-flag-checkered: "\f11e";
-@fa-var-flag-o: "\f11d";
-@fa-var-flash: "\f0e7";
-@fa-var-flask: "\f0c3";
-@fa-var-flickr: "\f16e";
-@fa-var-floppy-o: "\f0c7";
-@fa-var-folder: "\f07b";
-@fa-var-folder-o: "\f114";
-@fa-var-folder-open: "\f07c";
-@fa-var-folder-open-o: "\f115";
-@fa-var-font: "\f031";
-@fa-var-forumbee: "\f211";
-@fa-var-forward: "\f04e";
-@fa-var-foursquare: "\f180";
-@fa-var-frown-o: "\f119";
-@fa-var-futbol-o: "\f1e3";
-@fa-var-gamepad: "\f11b";
-@fa-var-gavel: "\f0e3";
-@fa-var-gbp: "\f154";
-@fa-var-ge: "\f1d1";
-@fa-var-gear: "\f013";
-@fa-var-gears: "\f085";
-@fa-var-genderless: "\f1db";
-@fa-var-gift: "\f06b";
-@fa-var-git: "\f1d3";
-@fa-var-git-square: "\f1d2";
-@fa-var-github: "\f09b";
-@fa-var-github-alt: "\f113";
-@fa-var-github-square: "\f092";
-@fa-var-gittip: "\f184";
-@fa-var-glass: "\f000";
-@fa-var-globe: "\f0ac";
-@fa-var-google: "\f1a0";
-@fa-var-google-plus: "\f0d5";
-@fa-var-google-plus-square: "\f0d4";
-@fa-var-google-wallet: "\f1ee";
-@fa-var-graduation-cap: "\f19d";
-@fa-var-gratipay: "\f184";
-@fa-var-group: "\f0c0";
-@fa-var-h-square: "\f0fd";
-@fa-var-hacker-news: "\f1d4";
-@fa-var-hand-o-down: "\f0a7";
-@fa-var-hand-o-left: "\f0a5";
-@fa-var-hand-o-right: "\f0a4";
-@fa-var-hand-o-up: "\f0a6";
-@fa-var-hdd-o: "\f0a0";
-@fa-var-header: "\f1dc";
-@fa-var-headphones: "\f025";
-@fa-var-heart: "\f004";
-@fa-var-heart-o: "\f08a";
-@fa-var-heartbeat: "\f21e";
-@fa-var-history: "\f1da";
-@fa-var-home: "\f015";
-@fa-var-hospital-o: "\f0f8";
-@fa-var-hotel: "\f236";
-@fa-var-html5: "\f13b";
-@fa-var-ils: "\f20b";
-@fa-var-image: "\f03e";
-@fa-var-inbox: "\f01c";
-@fa-var-indent: "\f03c";
-@fa-var-info: "\f129";
-@fa-var-info-circle: "\f05a";
-@fa-var-inr: "\f156";
-@fa-var-instagram: "\f16d";
-@fa-var-institution: "\f19c";
-@fa-var-ioxhost: "\f208";
-@fa-var-italic: "\f033";
-@fa-var-joomla: "\f1aa";
-@fa-var-jpy: "\f157";
-@fa-var-jsfiddle: "\f1cc";
-@fa-var-key: "\f084";
-@fa-var-keyboard-o: "\f11c";
-@fa-var-krw: "\f159";
-@fa-var-language: "\f1ab";
-@fa-var-laptop: "\f109";
-@fa-var-lastfm: "\f202";
-@fa-var-lastfm-square: "\f203";
-@fa-var-leaf: "\f06c";
-@fa-var-leanpub: "\f212";
-@fa-var-legal: "\f0e3";
-@fa-var-lemon-o: "\f094";
-@fa-var-level-down: "\f149";
-@fa-var-level-up: "\f148";
-@fa-var-life-bouy: "\f1cd";
-@fa-var-life-buoy: "\f1cd";
-@fa-var-life-ring: "\f1cd";
-@fa-var-life-saver: "\f1cd";
-@fa-var-lightbulb-o: "\f0eb";
-@fa-var-line-chart: "\f201";
-@fa-var-link: "\f0c1";
-@fa-var-linkedin: "\f0e1";
-@fa-var-linkedin-square: "\f08c";
-@fa-var-linux: "\f17c";
-@fa-var-list: "\f03a";
-@fa-var-list-alt: "\f022";
-@fa-var-list-ol: "\f0cb";
-@fa-var-list-ul: "\f0ca";
-@fa-var-location-arrow: "\f124";
-@fa-var-lock: "\f023";
-@fa-var-long-arrow-down: "\f175";
-@fa-var-long-arrow-left: "\f177";
-@fa-var-long-arrow-right: "\f178";
-@fa-var-long-arrow-up: "\f176";
-@fa-var-magic: "\f0d0";
-@fa-var-magnet: "\f076";
-@fa-var-mail-forward: "\f064";
-@fa-var-mail-reply: "\f112";
-@fa-var-mail-reply-all: "\f122";
-@fa-var-male: "\f183";
-@fa-var-map-marker: "\f041";
-@fa-var-mars: "\f222";
-@fa-var-mars-double: "\f227";
-@fa-var-mars-stroke: "\f229";
-@fa-var-mars-stroke-h: "\f22b";
-@fa-var-mars-stroke-v: "\f22a";
-@fa-var-maxcdn: "\f136";
-@fa-var-meanpath: "\f20c";
-@fa-var-medium: "\f23a";
-@fa-var-medkit: "\f0fa";
-@fa-var-meh-o: "\f11a";
-@fa-var-mercury: "\f223";
-@fa-var-microphone: "\f130";
-@fa-var-microphone-slash: "\f131";
-@fa-var-minus: "\f068";
-@fa-var-minus-circle: "\f056";
-@fa-var-minus-square: "\f146";
-@fa-var-minus-square-o: "\f147";
-@fa-var-mobile: "\f10b";
-@fa-var-mobile-phone: "\f10b";
-@fa-var-money: "\f0d6";
-@fa-var-moon-o: "\f186";
-@fa-var-mortar-board: "\f19d";
-@fa-var-motorcycle: "\f21c";
-@fa-var-music: "\f001";
-@fa-var-navicon: "\f0c9";
-@fa-var-neuter: "\f22c";
-@fa-var-newspaper-o: "\f1ea";
-@fa-var-openid: "\f19b";
-@fa-var-outdent: "\f03b";
-@fa-var-pagelines: "\f18c";
-@fa-var-paint-brush: "\f1fc";
-@fa-var-paper-plane: "\f1d8";
-@fa-var-paper-plane-o: "\f1d9";
-@fa-var-paperclip: "\f0c6";
-@fa-var-paragraph: "\f1dd";
-@fa-var-paste: "\f0ea";
-@fa-var-pause: "\f04c";
-@fa-var-paw: "\f1b0";
-@fa-var-paypal: "\f1ed";
-@fa-var-pencil: "\f040";
-@fa-var-pencil-square: "\f14b";
-@fa-var-pencil-square-o: "\f044";
-@fa-var-phone: "\f095";
-@fa-var-phone-square: "\f098";
-@fa-var-photo: "\f03e";
-@fa-var-picture-o: "\f03e";
-@fa-var-pie-chart: "\f200";
-@fa-var-pied-piper: "\f1a7";
-@fa-var-pied-piper-alt: "\f1a8";
-@fa-var-pinterest: "\f0d2";
-@fa-var-pinterest-p: "\f231";
-@fa-var-pinterest-square: "\f0d3";
-@fa-var-plane: "\f072";
-@fa-var-play: "\f04b";
-@fa-var-play-circle: "\f144";
-@fa-var-play-circle-o: "\f01d";
-@fa-var-plug: "\f1e6";
-@fa-var-plus: "\f067";
-@fa-var-plus-circle: "\f055";
-@fa-var-plus-square: "\f0fe";
-@fa-var-plus-square-o: "\f196";
-@fa-var-power-off: "\f011";
-@fa-var-print: "\f02f";
-@fa-var-puzzle-piece: "\f12e";
-@fa-var-qq: "\f1d6";
-@fa-var-qrcode: "\f029";
-@fa-var-question: "\f128";
-@fa-var-question-circle: "\f059";
-@fa-var-quote-left: "\f10d";
-@fa-var-quote-right: "\f10e";
-@fa-var-ra: "\f1d0";
-@fa-var-random: "\f074";
-@fa-var-rebel: "\f1d0";
-@fa-var-recycle: "\f1b8";
-@fa-var-reddit: "\f1a1";
-@fa-var-reddit-square: "\f1a2";
-@fa-var-refresh: "\f021";
-@fa-var-remove: "\f00d";
-@fa-var-renren: "\f18b";
-@fa-var-reorder: "\f0c9";
-@fa-var-repeat: "\f01e";
-@fa-var-reply: "\f112";
-@fa-var-reply-all: "\f122";
-@fa-var-retweet: "\f079";
-@fa-var-rmb: "\f157";
-@fa-var-road: "\f018";
-@fa-var-rocket: "\f135";
-@fa-var-rotate-left: "\f0e2";
-@fa-var-rotate-right: "\f01e";
-@fa-var-rouble: "\f158";
-@fa-var-rss: "\f09e";
-@fa-var-rss-square: "\f143";
-@fa-var-rub: "\f158";
-@fa-var-ruble: "\f158";
-@fa-var-rupee: "\f156";
-@fa-var-save: "\f0c7";
-@fa-var-scissors: "\f0c4";
-@fa-var-search: "\f002";
-@fa-var-search-minus: "\f010";
-@fa-var-search-plus: "\f00e";
-@fa-var-sellsy: "\f213";
-@fa-var-send: "\f1d8";
-@fa-var-send-o: "\f1d9";
-@fa-var-server: "\f233";
-@fa-var-share: "\f064";
-@fa-var-share-alt: "\f1e0";
-@fa-var-share-alt-square: "\f1e1";
-@fa-var-share-square: "\f14d";
-@fa-var-share-square-o: "\f045";
-@fa-var-shekel: "\f20b";
-@fa-var-sheqel: "\f20b";
-@fa-var-shield: "\f132";
-@fa-var-ship: "\f21a";
-@fa-var-shirtsinbulk: "\f214";
-@fa-var-shopping-cart: "\f07a";
-@fa-var-sign-in: "\f090";
-@fa-var-sign-out: "\f08b";
-@fa-var-signal: "\f012";
-@fa-var-simplybuilt: "\f215";
-@fa-var-sitemap: "\f0e8";
-@fa-var-skyatlas: "\f216";
-@fa-var-skype: "\f17e";
-@fa-var-slack: "\f198";
-@fa-var-sliders: "\f1de";
-@fa-var-slideshare: "\f1e7";
-@fa-var-smile-o: "\f118";
-@fa-var-soccer-ball-o: "\f1e3";
-@fa-var-sort: "\f0dc";
-@fa-var-sort-alpha-asc: "\f15d";
-@fa-var-sort-alpha-desc: "\f15e";
-@fa-var-sort-amount-asc: "\f160";
-@fa-var-sort-amount-desc: "\f161";
-@fa-var-sort-asc: "\f0de";
-@fa-var-sort-desc: "\f0dd";
-@fa-var-sort-down: "\f0dd";
-@fa-var-sort-numeric-asc: "\f162";
-@fa-var-sort-numeric-desc: "\f163";
-@fa-var-sort-up: "\f0de";
-@fa-var-soundcloud: "\f1be";
-@fa-var-space-shuttle: "\f197";
-@fa-var-spinner: "\f110";
-@fa-var-spoon: "\f1b1";
-@fa-var-spotify: "\f1bc";
-@fa-var-square: "\f0c8";
-@fa-var-square-o: "\f096";
-@fa-var-stack-exchange: "\f18d";
-@fa-var-stack-overflow: "\f16c";
-@fa-var-star: "\f005";
-@fa-var-star-half: "\f089";
-@fa-var-star-half-empty: "\f123";
-@fa-var-star-half-full: "\f123";
-@fa-var-star-half-o: "\f123";
-@fa-var-star-o: "\f006";
-@fa-var-steam: "\f1b6";
-@fa-var-steam-square: "\f1b7";
-@fa-var-step-backward: "\f048";
-@fa-var-step-forward: "\f051";
-@fa-var-stethoscope: "\f0f1";
-@fa-var-stop: "\f04d";
-@fa-var-street-view: "\f21d";
-@fa-var-strikethrough: "\f0cc";
-@fa-var-stumbleupon: "\f1a4";
-@fa-var-stumbleupon-circle: "\f1a3";
-@fa-var-subscript: "\f12c";
-@fa-var-subway: "\f239";
-@fa-var-suitcase: "\f0f2";
-@fa-var-sun-o: "\f185";
-@fa-var-superscript: "\f12b";
-@fa-var-support: "\f1cd";
-@fa-var-table: "\f0ce";
-@fa-var-tablet: "\f10a";
-@fa-var-tachometer: "\f0e4";
-@fa-var-tag: "\f02b";
-@fa-var-tags: "\f02c";
-@fa-var-tasks: "\f0ae";
-@fa-var-taxi: "\f1ba";
-@fa-var-tencent-weibo: "\f1d5";
-@fa-var-terminal: "\f120";
-@fa-var-text-height: "\f034";
-@fa-var-text-width: "\f035";
-@fa-var-th: "\f00a";
-@fa-var-th-large: "\f009";
-@fa-var-th-list: "\f00b";
-@fa-var-thumb-tack: "\f08d";
-@fa-var-thumbs-down: "\f165";
-@fa-var-thumbs-o-down: "\f088";
-@fa-var-thumbs-o-up: "\f087";
-@fa-var-thumbs-up: "\f164";
-@fa-var-ticket: "\f145";
-@fa-var-times: "\f00d";
-@fa-var-times-circle: "\f057";
-@fa-var-times-circle-o: "\f05c";
-@fa-var-tint: "\f043";
-@fa-var-toggle-down: "\f150";
-@fa-var-toggle-left: "\f191";
-@fa-var-toggle-off: "\f204";
-@fa-var-toggle-on: "\f205";
-@fa-var-toggle-right: "\f152";
-@fa-var-toggle-up: "\f151";
-@fa-var-train: "\f238";
-@fa-var-transgender: "\f224";
-@fa-var-transgender-alt: "\f225";
-@fa-var-trash: "\f1f8";
-@fa-var-trash-o: "\f014";
-@fa-var-tree: "\f1bb";
-@fa-var-trello: "\f181";
-@fa-var-trophy: "\f091";
-@fa-var-truck: "\f0d1";
-@fa-var-try: "\f195";
-@fa-var-tty: "\f1e4";
-@fa-var-tumblr: "\f173";
-@fa-var-tumblr-square: "\f174";
-@fa-var-turkish-lira: "\f195";
-@fa-var-twitch: "\f1e8";
-@fa-var-twitter: "\f099";
-@fa-var-twitter-square: "\f081";
-@fa-var-umbrella: "\f0e9";
-@fa-var-underline: "\f0cd";
-@fa-var-undo: "\f0e2";
-@fa-var-university: "\f19c";
-@fa-var-unlink: "\f127";
-@fa-var-unlock: "\f09c";
-@fa-var-unlock-alt: "\f13e";
-@fa-var-unsorted: "\f0dc";
-@fa-var-upload: "\f093";
-@fa-var-usd: "\f155";
-@fa-var-user: "\f007";
-@fa-var-user-md: "\f0f0";
-@fa-var-user-plus: "\f234";
-@fa-var-user-secret: "\f21b";
-@fa-var-user-times: "\f235";
-@fa-var-users: "\f0c0";
-@fa-var-venus: "\f221";
-@fa-var-venus-double: "\f226";
-@fa-var-venus-mars: "\f228";
-@fa-var-viacoin: "\f237";
-@fa-var-video-camera: "\f03d";
-@fa-var-vimeo-square: "\f194";
-@fa-var-vine: "\f1ca";
-@fa-var-vk: "\f189";
-@fa-var-volume-down: "\f027";
-@fa-var-volume-off: "\f026";
-@fa-var-volume-up: "\f028";
-@fa-var-warning: "\f071";
-@fa-var-wechat: "\f1d7";
-@fa-var-weibo: "\f18a";
-@fa-var-weixin: "\f1d7";
-@fa-var-whatsapp: "\f232";
-@fa-var-wheelchair: "\f193";
-@fa-var-wifi: "\f1eb";
-@fa-var-windows: "\f17a";
-@fa-var-won: "\f159";
-@fa-var-wordpress: "\f19a";
-@fa-var-wrench: "\f0ad";
-@fa-var-xing: "\f168";
-@fa-var-xing-square: "\f169";
-@fa-var-yahoo: "\f19e";
-@fa-var-yelp: "\f1e9";
-@fa-var-yen: "\f157";
-@fa-var-youtube: "\f167";
-@fa-var-youtube-play: "\f16a";
-@fa-var-youtube-square: "\f166";
-
diff --git a/presentation/contrib/font-awesome-4.3.0/scss/_animated.scss b/presentation/contrib/font-awesome-4.3.0/scss/_animated.scss
deleted file mode 100644
index 8a020dbff..000000000
--- a/presentation/contrib/font-awesome-4.3.0/scss/_animated.scss
+++ /dev/null
@@ -1,34 +0,0 @@
-// Spinning Icons
-// --------------------------
-
-.#{$fa-css-prefix}-spin {
- -webkit-animation: fa-spin 2s infinite linear;
- animation: fa-spin 2s infinite linear;
-}
-
-.#{$fa-css-prefix}-pulse {
- -webkit-animation: fa-spin 1s infinite steps(8);
- animation: fa-spin 1s infinite steps(8);
-}
-
-@-webkit-keyframes fa-spin {
- 0% {
- -webkit-transform: rotate(0deg);
- transform: rotate(0deg);
- }
- 100% {
- -webkit-transform: rotate(359deg);
- transform: rotate(359deg);
- }
-}
-
-@keyframes fa-spin {
- 0% {
- -webkit-transform: rotate(0deg);
- transform: rotate(0deg);
- }
- 100% {
- -webkit-transform: rotate(359deg);
- transform: rotate(359deg);
- }
-}
diff --git a/presentation/contrib/font-awesome-4.3.0/scss/_bordered-pulled.scss b/presentation/contrib/font-awesome-4.3.0/scss/_bordered-pulled.scss
deleted file mode 100644
index 9d3fdf3a0..000000000
--- a/presentation/contrib/font-awesome-4.3.0/scss/_bordered-pulled.scss
+++ /dev/null
@@ -1,16 +0,0 @@
-// Bordered & Pulled
-// -------------------------
-
-.#{$fa-css-prefix}-border {
- padding: .2em .25em .15em;
- border: solid .08em $fa-border-color;
- border-radius: .1em;
-}
-
-.pull-right { float: right; }
-.pull-left { float: left; }
-
-.#{$fa-css-prefix} {
- &.pull-left { margin-right: .3em; }
- &.pull-right { margin-left: .3em; }
-}
diff --git a/presentation/contrib/font-awesome-4.3.0/scss/_core.scss b/presentation/contrib/font-awesome-4.3.0/scss/_core.scss
deleted file mode 100644
index 5a2db9d56..000000000
--- a/presentation/contrib/font-awesome-4.3.0/scss/_core.scss
+++ /dev/null
@@ -1,13 +0,0 @@
-// Base Class Definition
-// -------------------------
-
-.#{$fa-css-prefix} {
- display: inline-block;
- font: normal normal normal #{$fa-font-size-base}/1 FontAwesome; // shortening font declaration
- font-size: inherit; // can't have font-size inherit on line above, so need to override
- text-rendering: auto; // optimizelegibility throws things off #1094
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- transform: translate(0, 0); // ensures no half-pixel rendering in firefox
-
-}
diff --git a/presentation/contrib/font-awesome-4.3.0/scss/_fixed-width.scss b/presentation/contrib/font-awesome-4.3.0/scss/_fixed-width.scss
deleted file mode 100644
index b221c9813..000000000
--- a/presentation/contrib/font-awesome-4.3.0/scss/_fixed-width.scss
+++ /dev/null
@@ -1,6 +0,0 @@
-// Fixed Width Icons
-// -------------------------
-.#{$fa-css-prefix}-fw {
- width: (18em / 14);
- text-align: center;
-}
diff --git a/presentation/contrib/font-awesome-4.3.0/scss/_icons.scss b/presentation/contrib/font-awesome-4.3.0/scss/_icons.scss
deleted file mode 100644
index fbcfe8123..000000000
--- a/presentation/contrib/font-awesome-4.3.0/scss/_icons.scss
+++ /dev/null
@@ -1,596 +0,0 @@
-/* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen
- readers do not read off random characters that represent icons */
-
-.#{$fa-css-prefix}-glass:before { content: $fa-var-glass; }
-.#{$fa-css-prefix}-music:before { content: $fa-var-music; }
-.#{$fa-css-prefix}-search:before { content: $fa-var-search; }
-.#{$fa-css-prefix}-envelope-o:before { content: $fa-var-envelope-o; }
-.#{$fa-css-prefix}-heart:before { content: $fa-var-heart; }
-.#{$fa-css-prefix}-star:before { content: $fa-var-star; }
-.#{$fa-css-prefix}-star-o:before { content: $fa-var-star-o; }
-.#{$fa-css-prefix}-user:before { content: $fa-var-user; }
-.#{$fa-css-prefix}-film:before { content: $fa-var-film; }
-.#{$fa-css-prefix}-th-large:before { content: $fa-var-th-large; }
-.#{$fa-css-prefix}-th:before { content: $fa-var-th; }
-.#{$fa-css-prefix}-th-list:before { content: $fa-var-th-list; }
-.#{$fa-css-prefix}-check:before { content: $fa-var-check; }
-.#{$fa-css-prefix}-remove:before,
-.#{$fa-css-prefix}-close:before,
-.#{$fa-css-prefix}-times:before { content: $fa-var-times; }
-.#{$fa-css-prefix}-search-plus:before { content: $fa-var-search-plus; }
-.#{$fa-css-prefix}-search-minus:before { content: $fa-var-search-minus; }
-.#{$fa-css-prefix}-power-off:before { content: $fa-var-power-off; }
-.#{$fa-css-prefix}-signal:before { content: $fa-var-signal; }
-.#{$fa-css-prefix}-gear:before,
-.#{$fa-css-prefix}-cog:before { content: $fa-var-cog; }
-.#{$fa-css-prefix}-trash-o:before { content: $fa-var-trash-o; }
-.#{$fa-css-prefix}-home:before { content: $fa-var-home; }
-.#{$fa-css-prefix}-file-o:before { content: $fa-var-file-o; }
-.#{$fa-css-prefix}-clock-o:before { content: $fa-var-clock-o; }
-.#{$fa-css-prefix}-road:before { content: $fa-var-road; }
-.#{$fa-css-prefix}-download:before { content: $fa-var-download; }
-.#{$fa-css-prefix}-arrow-circle-o-down:before { content: $fa-var-arrow-circle-o-down; }
-.#{$fa-css-prefix}-arrow-circle-o-up:before { content: $fa-var-arrow-circle-o-up; }
-.#{$fa-css-prefix}-inbox:before { content: $fa-var-inbox; }
-.#{$fa-css-prefix}-play-circle-o:before { content: $fa-var-play-circle-o; }
-.#{$fa-css-prefix}-rotate-right:before,
-.#{$fa-css-prefix}-repeat:before { content: $fa-var-repeat; }
-.#{$fa-css-prefix}-refresh:before { content: $fa-var-refresh; }
-.#{$fa-css-prefix}-list-alt:before { content: $fa-var-list-alt; }
-.#{$fa-css-prefix}-lock:before { content: $fa-var-lock; }
-.#{$fa-css-prefix}-flag:before { content: $fa-var-flag; }
-.#{$fa-css-prefix}-headphones:before { content: $fa-var-headphones; }
-.#{$fa-css-prefix}-volume-off:before { content: $fa-var-volume-off; }
-.#{$fa-css-prefix}-volume-down:before { content: $fa-var-volume-down; }
-.#{$fa-css-prefix}-volume-up:before { content: $fa-var-volume-up; }
-.#{$fa-css-prefix}-qrcode:before { content: $fa-var-qrcode; }
-.#{$fa-css-prefix}-barcode:before { content: $fa-var-barcode; }
-.#{$fa-css-prefix}-tag:before { content: $fa-var-tag; }
-.#{$fa-css-prefix}-tags:before { content: $fa-var-tags; }
-.#{$fa-css-prefix}-book:before { content: $fa-var-book; }
-.#{$fa-css-prefix}-bookmark:before { content: $fa-var-bookmark; }
-.#{$fa-css-prefix}-print:before { content: $fa-var-print; }
-.#{$fa-css-prefix}-camera:before { content: $fa-var-camera; }
-.#{$fa-css-prefix}-font:before { content: $fa-var-font; }
-.#{$fa-css-prefix}-bold:before { content: $fa-var-bold; }
-.#{$fa-css-prefix}-italic:before { content: $fa-var-italic; }
-.#{$fa-css-prefix}-text-height:before { content: $fa-var-text-height; }
-.#{$fa-css-prefix}-text-width:before { content: $fa-var-text-width; }
-.#{$fa-css-prefix}-align-left:before { content: $fa-var-align-left; }
-.#{$fa-css-prefix}-align-center:before { content: $fa-var-align-center; }
-.#{$fa-css-prefix}-align-right:before { content: $fa-var-align-right; }
-.#{$fa-css-prefix}-align-justify:before { content: $fa-var-align-justify; }
-.#{$fa-css-prefix}-list:before { content: $fa-var-list; }
-.#{$fa-css-prefix}-dedent:before,
-.#{$fa-css-prefix}-outdent:before { content: $fa-var-outdent; }
-.#{$fa-css-prefix}-indent:before { content: $fa-var-indent; }
-.#{$fa-css-prefix}-video-camera:before { content: $fa-var-video-camera; }
-.#{$fa-css-prefix}-photo:before,
-.#{$fa-css-prefix}-image:before,
-.#{$fa-css-prefix}-picture-o:before { content: $fa-var-picture-o; }
-.#{$fa-css-prefix}-pencil:before { content: $fa-var-pencil; }
-.#{$fa-css-prefix}-map-marker:before { content: $fa-var-map-marker; }
-.#{$fa-css-prefix}-adjust:before { content: $fa-var-adjust; }
-.#{$fa-css-prefix}-tint:before { content: $fa-var-tint; }
-.#{$fa-css-prefix}-edit:before,
-.#{$fa-css-prefix}-pencil-square-o:before { content: $fa-var-pencil-square-o; }
-.#{$fa-css-prefix}-share-square-o:before { content: $fa-var-share-square-o; }
-.#{$fa-css-prefix}-check-square-o:before { content: $fa-var-check-square-o; }
-.#{$fa-css-prefix}-arrows:before { content: $fa-var-arrows; }
-.#{$fa-css-prefix}-step-backward:before { content: $fa-var-step-backward; }
-.#{$fa-css-prefix}-fast-backward:before { content: $fa-var-fast-backward; }
-.#{$fa-css-prefix}-backward:before { content: $fa-var-backward; }
-.#{$fa-css-prefix}-play:before { content: $fa-var-play; }
-.#{$fa-css-prefix}-pause:before { content: $fa-var-pause; }
-.#{$fa-css-prefix}-stop:before { content: $fa-var-stop; }
-.#{$fa-css-prefix}-forward:before { content: $fa-var-forward; }
-.#{$fa-css-prefix}-fast-forward:before { content: $fa-var-fast-forward; }
-.#{$fa-css-prefix}-step-forward:before { content: $fa-var-step-forward; }
-.#{$fa-css-prefix}-eject:before { content: $fa-var-eject; }
-.#{$fa-css-prefix}-chevron-left:before { content: $fa-var-chevron-left; }
-.#{$fa-css-prefix}-chevron-right:before { content: $fa-var-chevron-right; }
-.#{$fa-css-prefix}-plus-circle:before { content: $fa-var-plus-circle; }
-.#{$fa-css-prefix}-minus-circle:before { content: $fa-var-minus-circle; }
-.#{$fa-css-prefix}-times-circle:before { content: $fa-var-times-circle; }
-.#{$fa-css-prefix}-check-circle:before { content: $fa-var-check-circle; }
-.#{$fa-css-prefix}-question-circle:before { content: $fa-var-question-circle; }
-.#{$fa-css-prefix}-info-circle:before { content: $fa-var-info-circle; }
-.#{$fa-css-prefix}-crosshairs:before { content: $fa-var-crosshairs; }
-.#{$fa-css-prefix}-times-circle-o:before { content: $fa-var-times-circle-o; }
-.#{$fa-css-prefix}-check-circle-o:before { content: $fa-var-check-circle-o; }
-.#{$fa-css-prefix}-ban:before { content: $fa-var-ban; }
-.#{$fa-css-prefix}-arrow-left:before { content: $fa-var-arrow-left; }
-.#{$fa-css-prefix}-arrow-right:before { content: $fa-var-arrow-right; }
-.#{$fa-css-prefix}-arrow-up:before { content: $fa-var-arrow-up; }
-.#{$fa-css-prefix}-arrow-down:before { content: $fa-var-arrow-down; }
-.#{$fa-css-prefix}-mail-forward:before,
-.#{$fa-css-prefix}-share:before { content: $fa-var-share; }
-.#{$fa-css-prefix}-expand:before { content: $fa-var-expand; }
-.#{$fa-css-prefix}-compress:before { content: $fa-var-compress; }
-.#{$fa-css-prefix}-plus:before { content: $fa-var-plus; }
-.#{$fa-css-prefix}-minus:before { content: $fa-var-minus; }
-.#{$fa-css-prefix}-asterisk:before { content: $fa-var-asterisk; }
-.#{$fa-css-prefix}-exclamation-circle:before { content: $fa-var-exclamation-circle; }
-.#{$fa-css-prefix}-gift:before { content: $fa-var-gift; }
-.#{$fa-css-prefix}-leaf:before { content: $fa-var-leaf; }
-.#{$fa-css-prefix}-fire:before { content: $fa-var-fire; }
-.#{$fa-css-prefix}-eye:before { content: $fa-var-eye; }
-.#{$fa-css-prefix}-eye-slash:before { content: $fa-var-eye-slash; }
-.#{$fa-css-prefix}-warning:before,
-.#{$fa-css-prefix}-exclamation-triangle:before { content: $fa-var-exclamation-triangle; }
-.#{$fa-css-prefix}-plane:before { content: $fa-var-plane; }
-.#{$fa-css-prefix}-calendar:before { content: $fa-var-calendar; }
-.#{$fa-css-prefix}-random:before { content: $fa-var-random; }
-.#{$fa-css-prefix}-comment:before { content: $fa-var-comment; }
-.#{$fa-css-prefix}-magnet:before { content: $fa-var-magnet; }
-.#{$fa-css-prefix}-chevron-up:before { content: $fa-var-chevron-up; }
-.#{$fa-css-prefix}-chevron-down:before { content: $fa-var-chevron-down; }
-.#{$fa-css-prefix}-retweet:before { content: $fa-var-retweet; }
-.#{$fa-css-prefix}-shopping-cart:before { content: $fa-var-shopping-cart; }
-.#{$fa-css-prefix}-folder:before { content: $fa-var-folder; }
-.#{$fa-css-prefix}-folder-open:before { content: $fa-var-folder-open; }
-.#{$fa-css-prefix}-arrows-v:before { content: $fa-var-arrows-v; }
-.#{$fa-css-prefix}-arrows-h:before { content: $fa-var-arrows-h; }
-.#{$fa-css-prefix}-bar-chart-o:before,
-.#{$fa-css-prefix}-bar-chart:before { content: $fa-var-bar-chart; }
-.#{$fa-css-prefix}-twitter-square:before { content: $fa-var-twitter-square; }
-.#{$fa-css-prefix}-facebook-square:before { content: $fa-var-facebook-square; }
-.#{$fa-css-prefix}-camera-retro:before { content: $fa-var-camera-retro; }
-.#{$fa-css-prefix}-key:before { content: $fa-var-key; }
-.#{$fa-css-prefix}-gears:before,
-.#{$fa-css-prefix}-cogs:before { content: $fa-var-cogs; }
-.#{$fa-css-prefix}-comments:before { content: $fa-var-comments; }
-.#{$fa-css-prefix}-thumbs-o-up:before { content: $fa-var-thumbs-o-up; }
-.#{$fa-css-prefix}-thumbs-o-down:before { content: $fa-var-thumbs-o-down; }
-.#{$fa-css-prefix}-star-half:before { content: $fa-var-star-half; }
-.#{$fa-css-prefix}-heart-o:before { content: $fa-var-heart-o; }
-.#{$fa-css-prefix}-sign-out:before { content: $fa-var-sign-out; }
-.#{$fa-css-prefix}-linkedin-square:before { content: $fa-var-linkedin-square; }
-.#{$fa-css-prefix}-thumb-tack:before { content: $fa-var-thumb-tack; }
-.#{$fa-css-prefix}-external-link:before { content: $fa-var-external-link; }
-.#{$fa-css-prefix}-sign-in:before { content: $fa-var-sign-in; }
-.#{$fa-css-prefix}-trophy:before { content: $fa-var-trophy; }
-.#{$fa-css-prefix}-github-square:before { content: $fa-var-github-square; }
-.#{$fa-css-prefix}-upload:before { content: $fa-var-upload; }
-.#{$fa-css-prefix}-lemon-o:before { content: $fa-var-lemon-o; }
-.#{$fa-css-prefix}-phone:before { content: $fa-var-phone; }
-.#{$fa-css-prefix}-square-o:before { content: $fa-var-square-o; }
-.#{$fa-css-prefix}-bookmark-o:before { content: $fa-var-bookmark-o; }
-.#{$fa-css-prefix}-phone-square:before { content: $fa-var-phone-square; }
-.#{$fa-css-prefix}-twitter:before { content: $fa-var-twitter; }
-.#{$fa-css-prefix}-facebook-f:before,
-.#{$fa-css-prefix}-facebook:before { content: $fa-var-facebook; }
-.#{$fa-css-prefix}-github:before { content: $fa-var-github; }
-.#{$fa-css-prefix}-unlock:before { content: $fa-var-unlock; }
-.#{$fa-css-prefix}-credit-card:before { content: $fa-var-credit-card; }
-.#{$fa-css-prefix}-rss:before { content: $fa-var-rss; }
-.#{$fa-css-prefix}-hdd-o:before { content: $fa-var-hdd-o; }
-.#{$fa-css-prefix}-bullhorn:before { content: $fa-var-bullhorn; }
-.#{$fa-css-prefix}-bell:before { content: $fa-var-bell; }
-.#{$fa-css-prefix}-certificate:before { content: $fa-var-certificate; }
-.#{$fa-css-prefix}-hand-o-right:before { content: $fa-var-hand-o-right; }
-.#{$fa-css-prefix}-hand-o-left:before { content: $fa-var-hand-o-left; }
-.#{$fa-css-prefix}-hand-o-up:before { content: $fa-var-hand-o-up; }
-.#{$fa-css-prefix}-hand-o-down:before { content: $fa-var-hand-o-down; }
-.#{$fa-css-prefix}-arrow-circle-left:before { content: $fa-var-arrow-circle-left; }
-.#{$fa-css-prefix}-arrow-circle-right:before { content: $fa-var-arrow-circle-right; }
-.#{$fa-css-prefix}-arrow-circle-up:before { content: $fa-var-arrow-circle-up; }
-.#{$fa-css-prefix}-arrow-circle-down:before { content: $fa-var-arrow-circle-down; }
-.#{$fa-css-prefix}-globe:before { content: $fa-var-globe; }
-.#{$fa-css-prefix}-wrench:before { content: $fa-var-wrench; }
-.#{$fa-css-prefix}-tasks:before { content: $fa-var-tasks; }
-.#{$fa-css-prefix}-filter:before { content: $fa-var-filter; }
-.#{$fa-css-prefix}-briefcase:before { content: $fa-var-briefcase; }
-.#{$fa-css-prefix}-arrows-alt:before { content: $fa-var-arrows-alt; }
-.#{$fa-css-prefix}-group:before,
-.#{$fa-css-prefix}-users:before { content: $fa-var-users; }
-.#{$fa-css-prefix}-chain:before,
-.#{$fa-css-prefix}-link:before { content: $fa-var-link; }
-.#{$fa-css-prefix}-cloud:before { content: $fa-var-cloud; }
-.#{$fa-css-prefix}-flask:before { content: $fa-var-flask; }
-.#{$fa-css-prefix}-cut:before,
-.#{$fa-css-prefix}-scissors:before { content: $fa-var-scissors; }
-.#{$fa-css-prefix}-copy:before,
-.#{$fa-css-prefix}-files-o:before { content: $fa-var-files-o; }
-.#{$fa-css-prefix}-paperclip:before { content: $fa-var-paperclip; }
-.#{$fa-css-prefix}-save:before,
-.#{$fa-css-prefix}-floppy-o:before { content: $fa-var-floppy-o; }
-.#{$fa-css-prefix}-square:before { content: $fa-var-square; }
-.#{$fa-css-prefix}-navicon:before,
-.#{$fa-css-prefix}-reorder:before,
-.#{$fa-css-prefix}-bars:before { content: $fa-var-bars; }
-.#{$fa-css-prefix}-list-ul:before { content: $fa-var-list-ul; }
-.#{$fa-css-prefix}-list-ol:before { content: $fa-var-list-ol; }
-.#{$fa-css-prefix}-strikethrough:before { content: $fa-var-strikethrough; }
-.#{$fa-css-prefix}-underline:before { content: $fa-var-underline; }
-.#{$fa-css-prefix}-table:before { content: $fa-var-table; }
-.#{$fa-css-prefix}-magic:before { content: $fa-var-magic; }
-.#{$fa-css-prefix}-truck:before { content: $fa-var-truck; }
-.#{$fa-css-prefix}-pinterest:before { content: $fa-var-pinterest; }
-.#{$fa-css-prefix}-pinterest-square:before { content: $fa-var-pinterest-square; }
-.#{$fa-css-prefix}-google-plus-square:before { content: $fa-var-google-plus-square; }
-.#{$fa-css-prefix}-google-plus:before { content: $fa-var-google-plus; }
-.#{$fa-css-prefix}-money:before { content: $fa-var-money; }
-.#{$fa-css-prefix}-caret-down:before { content: $fa-var-caret-down; }
-.#{$fa-css-prefix}-caret-up:before { content: $fa-var-caret-up; }
-.#{$fa-css-prefix}-caret-left:before { content: $fa-var-caret-left; }
-.#{$fa-css-prefix}-caret-right:before { content: $fa-var-caret-right; }
-.#{$fa-css-prefix}-columns:before { content: $fa-var-columns; }
-.#{$fa-css-prefix}-unsorted:before,
-.#{$fa-css-prefix}-sort:before { content: $fa-var-sort; }
-.#{$fa-css-prefix}-sort-down:before,
-.#{$fa-css-prefix}-sort-desc:before { content: $fa-var-sort-desc; }
-.#{$fa-css-prefix}-sort-up:before,
-.#{$fa-css-prefix}-sort-asc:before { content: $fa-var-sort-asc; }
-.#{$fa-css-prefix}-envelope:before { content: $fa-var-envelope; }
-.#{$fa-css-prefix}-linkedin:before { content: $fa-var-linkedin; }
-.#{$fa-css-prefix}-rotate-left:before,
-.#{$fa-css-prefix}-undo:before { content: $fa-var-undo; }
-.#{$fa-css-prefix}-legal:before,
-.#{$fa-css-prefix}-gavel:before { content: $fa-var-gavel; }
-.#{$fa-css-prefix}-dashboard:before,
-.#{$fa-css-prefix}-tachometer:before { content: $fa-var-tachometer; }
-.#{$fa-css-prefix}-comment-o:before { content: $fa-var-comment-o; }
-.#{$fa-css-prefix}-comments-o:before { content: $fa-var-comments-o; }
-.#{$fa-css-prefix}-flash:before,
-.#{$fa-css-prefix}-bolt:before { content: $fa-var-bolt; }
-.#{$fa-css-prefix}-sitemap:before { content: $fa-var-sitemap; }
-.#{$fa-css-prefix}-umbrella:before { content: $fa-var-umbrella; }
-.#{$fa-css-prefix}-paste:before,
-.#{$fa-css-prefix}-clipboard:before { content: $fa-var-clipboard; }
-.#{$fa-css-prefix}-lightbulb-o:before { content: $fa-var-lightbulb-o; }
-.#{$fa-css-prefix}-exchange:before { content: $fa-var-exchange; }
-.#{$fa-css-prefix}-cloud-download:before { content: $fa-var-cloud-download; }
-.#{$fa-css-prefix}-cloud-upload:before { content: $fa-var-cloud-upload; }
-.#{$fa-css-prefix}-user-md:before { content: $fa-var-user-md; }
-.#{$fa-css-prefix}-stethoscope:before { content: $fa-var-stethoscope; }
-.#{$fa-css-prefix}-suitcase:before { content: $fa-var-suitcase; }
-.#{$fa-css-prefix}-bell-o:before { content: $fa-var-bell-o; }
-.#{$fa-css-prefix}-coffee:before { content: $fa-var-coffee; }
-.#{$fa-css-prefix}-cutlery:before { content: $fa-var-cutlery; }
-.#{$fa-css-prefix}-file-text-o:before { content: $fa-var-file-text-o; }
-.#{$fa-css-prefix}-building-o:before { content: $fa-var-building-o; }
-.#{$fa-css-prefix}-hospital-o:before { content: $fa-var-hospital-o; }
-.#{$fa-css-prefix}-ambulance:before { content: $fa-var-ambulance; }
-.#{$fa-css-prefix}-medkit:before { content: $fa-var-medkit; }
-.#{$fa-css-prefix}-fighter-jet:before { content: $fa-var-fighter-jet; }
-.#{$fa-css-prefix}-beer:before { content: $fa-var-beer; }
-.#{$fa-css-prefix}-h-square:before { content: $fa-var-h-square; }
-.#{$fa-css-prefix}-plus-square:before { content: $fa-var-plus-square; }
-.#{$fa-css-prefix}-angle-double-left:before { content: $fa-var-angle-double-left; }
-.#{$fa-css-prefix}-angle-double-right:before { content: $fa-var-angle-double-right; }
-.#{$fa-css-prefix}-angle-double-up:before { content: $fa-var-angle-double-up; }
-.#{$fa-css-prefix}-angle-double-down:before { content: $fa-var-angle-double-down; }
-.#{$fa-css-prefix}-angle-left:before { content: $fa-var-angle-left; }
-.#{$fa-css-prefix}-angle-right:before { content: $fa-var-angle-right; }
-.#{$fa-css-prefix}-angle-up:before { content: $fa-var-angle-up; }
-.#{$fa-css-prefix}-angle-down:before { content: $fa-var-angle-down; }
-.#{$fa-css-prefix}-desktop:before { content: $fa-var-desktop; }
-.#{$fa-css-prefix}-laptop:before { content: $fa-var-laptop; }
-.#{$fa-css-prefix}-tablet:before { content: $fa-var-tablet; }
-.#{$fa-css-prefix}-mobile-phone:before,
-.#{$fa-css-prefix}-mobile:before { content: $fa-var-mobile; }
-.#{$fa-css-prefix}-circle-o:before { content: $fa-var-circle-o; }
-.#{$fa-css-prefix}-quote-left:before { content: $fa-var-quote-left; }
-.#{$fa-css-prefix}-quote-right:before { content: $fa-var-quote-right; }
-.#{$fa-css-prefix}-spinner:before { content: $fa-var-spinner; }
-.#{$fa-css-prefix}-circle:before { content: $fa-var-circle; }
-.#{$fa-css-prefix}-mail-reply:before,
-.#{$fa-css-prefix}-reply:before { content: $fa-var-reply; }
-.#{$fa-css-prefix}-github-alt:before { content: $fa-var-github-alt; }
-.#{$fa-css-prefix}-folder-o:before { content: $fa-var-folder-o; }
-.#{$fa-css-prefix}-folder-open-o:before { content: $fa-var-folder-open-o; }
-.#{$fa-css-prefix}-smile-o:before { content: $fa-var-smile-o; }
-.#{$fa-css-prefix}-frown-o:before { content: $fa-var-frown-o; }
-.#{$fa-css-prefix}-meh-o:before { content: $fa-var-meh-o; }
-.#{$fa-css-prefix}-gamepad:before { content: $fa-var-gamepad; }
-.#{$fa-css-prefix}-keyboard-o:before { content: $fa-var-keyboard-o; }
-.#{$fa-css-prefix}-flag-o:before { content: $fa-var-flag-o; }
-.#{$fa-css-prefix}-flag-checkered:before { content: $fa-var-flag-checkered; }
-.#{$fa-css-prefix}-terminal:before { content: $fa-var-terminal; }
-.#{$fa-css-prefix}-code:before { content: $fa-var-code; }
-.#{$fa-css-prefix}-mail-reply-all:before,
-.#{$fa-css-prefix}-reply-all:before { content: $fa-var-reply-all; }
-.#{$fa-css-prefix}-star-half-empty:before,
-.#{$fa-css-prefix}-star-half-full:before,
-.#{$fa-css-prefix}-star-half-o:before { content: $fa-var-star-half-o; }
-.#{$fa-css-prefix}-location-arrow:before { content: $fa-var-location-arrow; }
-.#{$fa-css-prefix}-crop:before { content: $fa-var-crop; }
-.#{$fa-css-prefix}-code-fork:before { content: $fa-var-code-fork; }
-.#{$fa-css-prefix}-unlink:before,
-.#{$fa-css-prefix}-chain-broken:before { content: $fa-var-chain-broken; }
-.#{$fa-css-prefix}-question:before { content: $fa-var-question; }
-.#{$fa-css-prefix}-info:before { content: $fa-var-info; }
-.#{$fa-css-prefix}-exclamation:before { content: $fa-var-exclamation; }
-.#{$fa-css-prefix}-superscript:before { content: $fa-var-superscript; }
-.#{$fa-css-prefix}-subscript:before { content: $fa-var-subscript; }
-.#{$fa-css-prefix}-eraser:before { content: $fa-var-eraser; }
-.#{$fa-css-prefix}-puzzle-piece:before { content: $fa-var-puzzle-piece; }
-.#{$fa-css-prefix}-microphone:before { content: $fa-var-microphone; }
-.#{$fa-css-prefix}-microphone-slash:before { content: $fa-var-microphone-slash; }
-.#{$fa-css-prefix}-shield:before { content: $fa-var-shield; }
-.#{$fa-css-prefix}-calendar-o:before { content: $fa-var-calendar-o; }
-.#{$fa-css-prefix}-fire-extinguisher:before { content: $fa-var-fire-extinguisher; }
-.#{$fa-css-prefix}-rocket:before { content: $fa-var-rocket; }
-.#{$fa-css-prefix}-maxcdn:before { content: $fa-var-maxcdn; }
-.#{$fa-css-prefix}-chevron-circle-left:before { content: $fa-var-chevron-circle-left; }
-.#{$fa-css-prefix}-chevron-circle-right:before { content: $fa-var-chevron-circle-right; }
-.#{$fa-css-prefix}-chevron-circle-up:before { content: $fa-var-chevron-circle-up; }
-.#{$fa-css-prefix}-chevron-circle-down:before { content: $fa-var-chevron-circle-down; }
-.#{$fa-css-prefix}-html5:before { content: $fa-var-html5; }
-.#{$fa-css-prefix}-css3:before { content: $fa-var-css3; }
-.#{$fa-css-prefix}-anchor:before { content: $fa-var-anchor; }
-.#{$fa-css-prefix}-unlock-alt:before { content: $fa-var-unlock-alt; }
-.#{$fa-css-prefix}-bullseye:before { content: $fa-var-bullseye; }
-.#{$fa-css-prefix}-ellipsis-h:before { content: $fa-var-ellipsis-h; }
-.#{$fa-css-prefix}-ellipsis-v:before { content: $fa-var-ellipsis-v; }
-.#{$fa-css-prefix}-rss-square:before { content: $fa-var-rss-square; }
-.#{$fa-css-prefix}-play-circle:before { content: $fa-var-play-circle; }
-.#{$fa-css-prefix}-ticket:before { content: $fa-var-ticket; }
-.#{$fa-css-prefix}-minus-square:before { content: $fa-var-minus-square; }
-.#{$fa-css-prefix}-minus-square-o:before { content: $fa-var-minus-square-o; }
-.#{$fa-css-prefix}-level-up:before { content: $fa-var-level-up; }
-.#{$fa-css-prefix}-level-down:before { content: $fa-var-level-down; }
-.#{$fa-css-prefix}-check-square:before { content: $fa-var-check-square; }
-.#{$fa-css-prefix}-pencil-square:before { content: $fa-var-pencil-square; }
-.#{$fa-css-prefix}-external-link-square:before { content: $fa-var-external-link-square; }
-.#{$fa-css-prefix}-share-square:before { content: $fa-var-share-square; }
-.#{$fa-css-prefix}-compass:before { content: $fa-var-compass; }
-.#{$fa-css-prefix}-toggle-down:before,
-.#{$fa-css-prefix}-caret-square-o-down:before { content: $fa-var-caret-square-o-down; }
-.#{$fa-css-prefix}-toggle-up:before,
-.#{$fa-css-prefix}-caret-square-o-up:before { content: $fa-var-caret-square-o-up; }
-.#{$fa-css-prefix}-toggle-right:before,
-.#{$fa-css-prefix}-caret-square-o-right:before { content: $fa-var-caret-square-o-right; }
-.#{$fa-css-prefix}-euro:before,
-.#{$fa-css-prefix}-eur:before { content: $fa-var-eur; }
-.#{$fa-css-prefix}-gbp:before { content: $fa-var-gbp; }
-.#{$fa-css-prefix}-dollar:before,
-.#{$fa-css-prefix}-usd:before { content: $fa-var-usd; }
-.#{$fa-css-prefix}-rupee:before,
-.#{$fa-css-prefix}-inr:before { content: $fa-var-inr; }
-.#{$fa-css-prefix}-cny:before,
-.#{$fa-css-prefix}-rmb:before,
-.#{$fa-css-prefix}-yen:before,
-.#{$fa-css-prefix}-jpy:before { content: $fa-var-jpy; }
-.#{$fa-css-prefix}-ruble:before,
-.#{$fa-css-prefix}-rouble:before,
-.#{$fa-css-prefix}-rub:before { content: $fa-var-rub; }
-.#{$fa-css-prefix}-won:before,
-.#{$fa-css-prefix}-krw:before { content: $fa-var-krw; }
-.#{$fa-css-prefix}-bitcoin:before,
-.#{$fa-css-prefix}-btc:before { content: $fa-var-btc; }
-.#{$fa-css-prefix}-file:before { content: $fa-var-file; }
-.#{$fa-css-prefix}-file-text:before { content: $fa-var-file-text; }
-.#{$fa-css-prefix}-sort-alpha-asc:before { content: $fa-var-sort-alpha-asc; }
-.#{$fa-css-prefix}-sort-alpha-desc:before { content: $fa-var-sort-alpha-desc; }
-.#{$fa-css-prefix}-sort-amount-asc:before { content: $fa-var-sort-amount-asc; }
-.#{$fa-css-prefix}-sort-amount-desc:before { content: $fa-var-sort-amount-desc; }
-.#{$fa-css-prefix}-sort-numeric-asc:before { content: $fa-var-sort-numeric-asc; }
-.#{$fa-css-prefix}-sort-numeric-desc:before { content: $fa-var-sort-numeric-desc; }
-.#{$fa-css-prefix}-thumbs-up:before { content: $fa-var-thumbs-up; }
-.#{$fa-css-prefix}-thumbs-down:before { content: $fa-var-thumbs-down; }
-.#{$fa-css-prefix}-youtube-square:before { content: $fa-var-youtube-square; }
-.#{$fa-css-prefix}-youtube:before { content: $fa-var-youtube; }
-.#{$fa-css-prefix}-xing:before { content: $fa-var-xing; }
-.#{$fa-css-prefix}-xing-square:before { content: $fa-var-xing-square; }
-.#{$fa-css-prefix}-youtube-play:before { content: $fa-var-youtube-play; }
-.#{$fa-css-prefix}-dropbox:before { content: $fa-var-dropbox; }
-.#{$fa-css-prefix}-stack-overflow:before { content: $fa-var-stack-overflow; }
-.#{$fa-css-prefix}-instagram:before { content: $fa-var-instagram; }
-.#{$fa-css-prefix}-flickr:before { content: $fa-var-flickr; }
-.#{$fa-css-prefix}-adn:before { content: $fa-var-adn; }
-.#{$fa-css-prefix}-bitbucket:before { content: $fa-var-bitbucket; }
-.#{$fa-css-prefix}-bitbucket-square:before { content: $fa-var-bitbucket-square; }
-.#{$fa-css-prefix}-tumblr:before { content: $fa-var-tumblr; }
-.#{$fa-css-prefix}-tumblr-square:before { content: $fa-var-tumblr-square; }
-.#{$fa-css-prefix}-long-arrow-down:before { content: $fa-var-long-arrow-down; }
-.#{$fa-css-prefix}-long-arrow-up:before { content: $fa-var-long-arrow-up; }
-.#{$fa-css-prefix}-long-arrow-left:before { content: $fa-var-long-arrow-left; }
-.#{$fa-css-prefix}-long-arrow-right:before { content: $fa-var-long-arrow-right; }
-.#{$fa-css-prefix}-apple:before { content: $fa-var-apple; }
-.#{$fa-css-prefix}-windows:before { content: $fa-var-windows; }
-.#{$fa-css-prefix}-android:before { content: $fa-var-android; }
-.#{$fa-css-prefix}-linux:before { content: $fa-var-linux; }
-.#{$fa-css-prefix}-dribbble:before { content: $fa-var-dribbble; }
-.#{$fa-css-prefix}-skype:before { content: $fa-var-skype; }
-.#{$fa-css-prefix}-foursquare:before { content: $fa-var-foursquare; }
-.#{$fa-css-prefix}-trello:before { content: $fa-var-trello; }
-.#{$fa-css-prefix}-female:before { content: $fa-var-female; }
-.#{$fa-css-prefix}-male:before { content: $fa-var-male; }
-.#{$fa-css-prefix}-gittip:before,
-.#{$fa-css-prefix}-gratipay:before { content: $fa-var-gratipay; }
-.#{$fa-css-prefix}-sun-o:before { content: $fa-var-sun-o; }
-.#{$fa-css-prefix}-moon-o:before { content: $fa-var-moon-o; }
-.#{$fa-css-prefix}-archive:before { content: $fa-var-archive; }
-.#{$fa-css-prefix}-bug:before { content: $fa-var-bug; }
-.#{$fa-css-prefix}-vk:before { content: $fa-var-vk; }
-.#{$fa-css-prefix}-weibo:before { content: $fa-var-weibo; }
-.#{$fa-css-prefix}-renren:before { content: $fa-var-renren; }
-.#{$fa-css-prefix}-pagelines:before { content: $fa-var-pagelines; }
-.#{$fa-css-prefix}-stack-exchange:before { content: $fa-var-stack-exchange; }
-.#{$fa-css-prefix}-arrow-circle-o-right:before { content: $fa-var-arrow-circle-o-right; }
-.#{$fa-css-prefix}-arrow-circle-o-left:before { content: $fa-var-arrow-circle-o-left; }
-.#{$fa-css-prefix}-toggle-left:before,
-.#{$fa-css-prefix}-caret-square-o-left:before { content: $fa-var-caret-square-o-left; }
-.#{$fa-css-prefix}-dot-circle-o:before { content: $fa-var-dot-circle-o; }
-.#{$fa-css-prefix}-wheelchair:before { content: $fa-var-wheelchair; }
-.#{$fa-css-prefix}-vimeo-square:before { content: $fa-var-vimeo-square; }
-.#{$fa-css-prefix}-turkish-lira:before,
-.#{$fa-css-prefix}-try:before { content: $fa-var-try; }
-.#{$fa-css-prefix}-plus-square-o:before { content: $fa-var-plus-square-o; }
-.#{$fa-css-prefix}-space-shuttle:before { content: $fa-var-space-shuttle; }
-.#{$fa-css-prefix}-slack:before { content: $fa-var-slack; }
-.#{$fa-css-prefix}-envelope-square:before { content: $fa-var-envelope-square; }
-.#{$fa-css-prefix}-wordpress:before { content: $fa-var-wordpress; }
-.#{$fa-css-prefix}-openid:before { content: $fa-var-openid; }
-.#{$fa-css-prefix}-institution:before,
-.#{$fa-css-prefix}-bank:before,
-.#{$fa-css-prefix}-university:before { content: $fa-var-university; }
-.#{$fa-css-prefix}-mortar-board:before,
-.#{$fa-css-prefix}-graduation-cap:before { content: $fa-var-graduation-cap; }
-.#{$fa-css-prefix}-yahoo:before { content: $fa-var-yahoo; }
-.#{$fa-css-prefix}-google:before { content: $fa-var-google; }
-.#{$fa-css-prefix}-reddit:before { content: $fa-var-reddit; }
-.#{$fa-css-prefix}-reddit-square:before { content: $fa-var-reddit-square; }
-.#{$fa-css-prefix}-stumbleupon-circle:before { content: $fa-var-stumbleupon-circle; }
-.#{$fa-css-prefix}-stumbleupon:before { content: $fa-var-stumbleupon; }
-.#{$fa-css-prefix}-delicious:before { content: $fa-var-delicious; }
-.#{$fa-css-prefix}-digg:before { content: $fa-var-digg; }
-.#{$fa-css-prefix}-pied-piper:before { content: $fa-var-pied-piper; }
-.#{$fa-css-prefix}-pied-piper-alt:before { content: $fa-var-pied-piper-alt; }
-.#{$fa-css-prefix}-drupal:before { content: $fa-var-drupal; }
-.#{$fa-css-prefix}-joomla:before { content: $fa-var-joomla; }
-.#{$fa-css-prefix}-language:before { content: $fa-var-language; }
-.#{$fa-css-prefix}-fax:before { content: $fa-var-fax; }
-.#{$fa-css-prefix}-building:before { content: $fa-var-building; }
-.#{$fa-css-prefix}-child:before { content: $fa-var-child; }
-.#{$fa-css-prefix}-paw:before { content: $fa-var-paw; }
-.#{$fa-css-prefix}-spoon:before { content: $fa-var-spoon; }
-.#{$fa-css-prefix}-cube:before { content: $fa-var-cube; }
-.#{$fa-css-prefix}-cubes:before { content: $fa-var-cubes; }
-.#{$fa-css-prefix}-behance:before { content: $fa-var-behance; }
-.#{$fa-css-prefix}-behance-square:before { content: $fa-var-behance-square; }
-.#{$fa-css-prefix}-steam:before { content: $fa-var-steam; }
-.#{$fa-css-prefix}-steam-square:before { content: $fa-var-steam-square; }
-.#{$fa-css-prefix}-recycle:before { content: $fa-var-recycle; }
-.#{$fa-css-prefix}-automobile:before,
-.#{$fa-css-prefix}-car:before { content: $fa-var-car; }
-.#{$fa-css-prefix}-cab:before,
-.#{$fa-css-prefix}-taxi:before { content: $fa-var-taxi; }
-.#{$fa-css-prefix}-tree:before { content: $fa-var-tree; }
-.#{$fa-css-prefix}-spotify:before { content: $fa-var-spotify; }
-.#{$fa-css-prefix}-deviantart:before { content: $fa-var-deviantart; }
-.#{$fa-css-prefix}-soundcloud:before { content: $fa-var-soundcloud; }
-.#{$fa-css-prefix}-database:before { content: $fa-var-database; }
-.#{$fa-css-prefix}-file-pdf-o:before { content: $fa-var-file-pdf-o; }
-.#{$fa-css-prefix}-file-word-o:before { content: $fa-var-file-word-o; }
-.#{$fa-css-prefix}-file-excel-o:before { content: $fa-var-file-excel-o; }
-.#{$fa-css-prefix}-file-powerpoint-o:before { content: $fa-var-file-powerpoint-o; }
-.#{$fa-css-prefix}-file-photo-o:before,
-.#{$fa-css-prefix}-file-picture-o:before,
-.#{$fa-css-prefix}-file-image-o:before { content: $fa-var-file-image-o; }
-.#{$fa-css-prefix}-file-zip-o:before,
-.#{$fa-css-prefix}-file-archive-o:before { content: $fa-var-file-archive-o; }
-.#{$fa-css-prefix}-file-sound-o:before,
-.#{$fa-css-prefix}-file-audio-o:before { content: $fa-var-file-audio-o; }
-.#{$fa-css-prefix}-file-movie-o:before,
-.#{$fa-css-prefix}-file-video-o:before { content: $fa-var-file-video-o; }
-.#{$fa-css-prefix}-file-code-o:before { content: $fa-var-file-code-o; }
-.#{$fa-css-prefix}-vine:before { content: $fa-var-vine; }
-.#{$fa-css-prefix}-codepen:before { content: $fa-var-codepen; }
-.#{$fa-css-prefix}-jsfiddle:before { content: $fa-var-jsfiddle; }
-.#{$fa-css-prefix}-life-bouy:before,
-.#{$fa-css-prefix}-life-buoy:before,
-.#{$fa-css-prefix}-life-saver:before,
-.#{$fa-css-prefix}-support:before,
-.#{$fa-css-prefix}-life-ring:before { content: $fa-var-life-ring; }
-.#{$fa-css-prefix}-circle-o-notch:before { content: $fa-var-circle-o-notch; }
-.#{$fa-css-prefix}-ra:before,
-.#{$fa-css-prefix}-rebel:before { content: $fa-var-rebel; }
-.#{$fa-css-prefix}-ge:before,
-.#{$fa-css-prefix}-empire:before { content: $fa-var-empire; }
-.#{$fa-css-prefix}-git-square:before { content: $fa-var-git-square; }
-.#{$fa-css-prefix}-git:before { content: $fa-var-git; }
-.#{$fa-css-prefix}-hacker-news:before { content: $fa-var-hacker-news; }
-.#{$fa-css-prefix}-tencent-weibo:before { content: $fa-var-tencent-weibo; }
-.#{$fa-css-prefix}-qq:before { content: $fa-var-qq; }
-.#{$fa-css-prefix}-wechat:before,
-.#{$fa-css-prefix}-weixin:before { content: $fa-var-weixin; }
-.#{$fa-css-prefix}-send:before,
-.#{$fa-css-prefix}-paper-plane:before { content: $fa-var-paper-plane; }
-.#{$fa-css-prefix}-send-o:before,
-.#{$fa-css-prefix}-paper-plane-o:before { content: $fa-var-paper-plane-o; }
-.#{$fa-css-prefix}-history:before { content: $fa-var-history; }
-.#{$fa-css-prefix}-genderless:before,
-.#{$fa-css-prefix}-circle-thin:before { content: $fa-var-circle-thin; }
-.#{$fa-css-prefix}-header:before { content: $fa-var-header; }
-.#{$fa-css-prefix}-paragraph:before { content: $fa-var-paragraph; }
-.#{$fa-css-prefix}-sliders:before { content: $fa-var-sliders; }
-.#{$fa-css-prefix}-share-alt:before { content: $fa-var-share-alt; }
-.#{$fa-css-prefix}-share-alt-square:before { content: $fa-var-share-alt-square; }
-.#{$fa-css-prefix}-bomb:before { content: $fa-var-bomb; }
-.#{$fa-css-prefix}-soccer-ball-o:before,
-.#{$fa-css-prefix}-futbol-o:before { content: $fa-var-futbol-o; }
-.#{$fa-css-prefix}-tty:before { content: $fa-var-tty; }
-.#{$fa-css-prefix}-binoculars:before { content: $fa-var-binoculars; }
-.#{$fa-css-prefix}-plug:before { content: $fa-var-plug; }
-.#{$fa-css-prefix}-slideshare:before { content: $fa-var-slideshare; }
-.#{$fa-css-prefix}-twitch:before { content: $fa-var-twitch; }
-.#{$fa-css-prefix}-yelp:before { content: $fa-var-yelp; }
-.#{$fa-css-prefix}-newspaper-o:before { content: $fa-var-newspaper-o; }
-.#{$fa-css-prefix}-wifi:before { content: $fa-var-wifi; }
-.#{$fa-css-prefix}-calculator:before { content: $fa-var-calculator; }
-.#{$fa-css-prefix}-paypal:before { content: $fa-var-paypal; }
-.#{$fa-css-prefix}-google-wallet:before { content: $fa-var-google-wallet; }
-.#{$fa-css-prefix}-cc-visa:before { content: $fa-var-cc-visa; }
-.#{$fa-css-prefix}-cc-mastercard:before { content: $fa-var-cc-mastercard; }
-.#{$fa-css-prefix}-cc-discover:before { content: $fa-var-cc-discover; }
-.#{$fa-css-prefix}-cc-amex:before { content: $fa-var-cc-amex; }
-.#{$fa-css-prefix}-cc-paypal:before { content: $fa-var-cc-paypal; }
-.#{$fa-css-prefix}-cc-stripe:before { content: $fa-var-cc-stripe; }
-.#{$fa-css-prefix}-bell-slash:before { content: $fa-var-bell-slash; }
-.#{$fa-css-prefix}-bell-slash-o:before { content: $fa-var-bell-slash-o; }
-.#{$fa-css-prefix}-trash:before { content: $fa-var-trash; }
-.#{$fa-css-prefix}-copyright:before { content: $fa-var-copyright; }
-.#{$fa-css-prefix}-at:before { content: $fa-var-at; }
-.#{$fa-css-prefix}-eyedropper:before { content: $fa-var-eyedropper; }
-.#{$fa-css-prefix}-paint-brush:before { content: $fa-var-paint-brush; }
-.#{$fa-css-prefix}-birthday-cake:before { content: $fa-var-birthday-cake; }
-.#{$fa-css-prefix}-area-chart:before { content: $fa-var-area-chart; }
-.#{$fa-css-prefix}-pie-chart:before { content: $fa-var-pie-chart; }
-.#{$fa-css-prefix}-line-chart:before { content: $fa-var-line-chart; }
-.#{$fa-css-prefix}-lastfm:before { content: $fa-var-lastfm; }
-.#{$fa-css-prefix}-lastfm-square:before { content: $fa-var-lastfm-square; }
-.#{$fa-css-prefix}-toggle-off:before { content: $fa-var-toggle-off; }
-.#{$fa-css-prefix}-toggle-on:before { content: $fa-var-toggle-on; }
-.#{$fa-css-prefix}-bicycle:before { content: $fa-var-bicycle; }
-.#{$fa-css-prefix}-bus:before { content: $fa-var-bus; }
-.#{$fa-css-prefix}-ioxhost:before { content: $fa-var-ioxhost; }
-.#{$fa-css-prefix}-angellist:before { content: $fa-var-angellist; }
-.#{$fa-css-prefix}-cc:before { content: $fa-var-cc; }
-.#{$fa-css-prefix}-shekel:before,
-.#{$fa-css-prefix}-sheqel:before,
-.#{$fa-css-prefix}-ils:before { content: $fa-var-ils; }
-.#{$fa-css-prefix}-meanpath:before { content: $fa-var-meanpath; }
-.#{$fa-css-prefix}-buysellads:before { content: $fa-var-buysellads; }
-.#{$fa-css-prefix}-connectdevelop:before { content: $fa-var-connectdevelop; }
-.#{$fa-css-prefix}-dashcube:before { content: $fa-var-dashcube; }
-.#{$fa-css-prefix}-forumbee:before { content: $fa-var-forumbee; }
-.#{$fa-css-prefix}-leanpub:before { content: $fa-var-leanpub; }
-.#{$fa-css-prefix}-sellsy:before { content: $fa-var-sellsy; }
-.#{$fa-css-prefix}-shirtsinbulk:before { content: $fa-var-shirtsinbulk; }
-.#{$fa-css-prefix}-simplybuilt:before { content: $fa-var-simplybuilt; }
-.#{$fa-css-prefix}-skyatlas:before { content: $fa-var-skyatlas; }
-.#{$fa-css-prefix}-cart-plus:before { content: $fa-var-cart-plus; }
-.#{$fa-css-prefix}-cart-arrow-down:before { content: $fa-var-cart-arrow-down; }
-.#{$fa-css-prefix}-diamond:before { content: $fa-var-diamond; }
-.#{$fa-css-prefix}-ship:before { content: $fa-var-ship; }
-.#{$fa-css-prefix}-user-secret:before { content: $fa-var-user-secret; }
-.#{$fa-css-prefix}-motorcycle:before { content: $fa-var-motorcycle; }
-.#{$fa-css-prefix}-street-view:before { content: $fa-var-street-view; }
-.#{$fa-css-prefix}-heartbeat:before { content: $fa-var-heartbeat; }
-.#{$fa-css-prefix}-venus:before { content: $fa-var-venus; }
-.#{$fa-css-prefix}-mars:before { content: $fa-var-mars; }
-.#{$fa-css-prefix}-mercury:before { content: $fa-var-mercury; }
-.#{$fa-css-prefix}-transgender:before { content: $fa-var-transgender; }
-.#{$fa-css-prefix}-transgender-alt:before { content: $fa-var-transgender-alt; }
-.#{$fa-css-prefix}-venus-double:before { content: $fa-var-venus-double; }
-.#{$fa-css-prefix}-mars-double:before { content: $fa-var-mars-double; }
-.#{$fa-css-prefix}-venus-mars:before { content: $fa-var-venus-mars; }
-.#{$fa-css-prefix}-mars-stroke:before { content: $fa-var-mars-stroke; }
-.#{$fa-css-prefix}-mars-stroke-v:before { content: $fa-var-mars-stroke-v; }
-.#{$fa-css-prefix}-mars-stroke-h:before { content: $fa-var-mars-stroke-h; }
-.#{$fa-css-prefix}-neuter:before { content: $fa-var-neuter; }
-.#{$fa-css-prefix}-facebook-official:before { content: $fa-var-facebook-official; }
-.#{$fa-css-prefix}-pinterest-p:before { content: $fa-var-pinterest-p; }
-.#{$fa-css-prefix}-whatsapp:before { content: $fa-var-whatsapp; }
-.#{$fa-css-prefix}-server:before { content: $fa-var-server; }
-.#{$fa-css-prefix}-user-plus:before { content: $fa-var-user-plus; }
-.#{$fa-css-prefix}-user-times:before { content: $fa-var-user-times; }
-.#{$fa-css-prefix}-hotel:before,
-.#{$fa-css-prefix}-bed:before { content: $fa-var-bed; }
-.#{$fa-css-prefix}-viacoin:before { content: $fa-var-viacoin; }
-.#{$fa-css-prefix}-train:before { content: $fa-var-train; }
-.#{$fa-css-prefix}-subway:before { content: $fa-var-subway; }
-.#{$fa-css-prefix}-medium:before { content: $fa-var-medium; }
diff --git a/presentation/contrib/font-awesome-4.3.0/scss/_larger.scss b/presentation/contrib/font-awesome-4.3.0/scss/_larger.scss
deleted file mode 100644
index 41e9a8184..000000000
--- a/presentation/contrib/font-awesome-4.3.0/scss/_larger.scss
+++ /dev/null
@@ -1,13 +0,0 @@
-// Icon Sizes
-// -------------------------
-
-/* makes the font 33% larger relative to the icon container */
-.#{$fa-css-prefix}-lg {
- font-size: (4em / 3);
- line-height: (3em / 4);
- vertical-align: -15%;
-}
-.#{$fa-css-prefix}-2x { font-size: 2em; }
-.#{$fa-css-prefix}-3x { font-size: 3em; }
-.#{$fa-css-prefix}-4x { font-size: 4em; }
-.#{$fa-css-prefix}-5x { font-size: 5em; }
diff --git a/presentation/contrib/font-awesome-4.3.0/scss/_list.scss b/presentation/contrib/font-awesome-4.3.0/scss/_list.scss
deleted file mode 100644
index 7d1e4d54d..000000000
--- a/presentation/contrib/font-awesome-4.3.0/scss/_list.scss
+++ /dev/null
@@ -1,19 +0,0 @@
-// List Icons
-// -------------------------
-
-.#{$fa-css-prefix}-ul {
- padding-left: 0;
- margin-left: $fa-li-width;
- list-style-type: none;
- > li { position: relative; }
-}
-.#{$fa-css-prefix}-li {
- position: absolute;
- left: -$fa-li-width;
- width: $fa-li-width;
- top: (2em / 14);
- text-align: center;
- &.#{$fa-css-prefix}-lg {
- left: -$fa-li-width + (4em / 14);
- }
-}
diff --git a/presentation/contrib/font-awesome-4.3.0/scss/_mixins.scss b/presentation/contrib/font-awesome-4.3.0/scss/_mixins.scss
deleted file mode 100644
index 6b7f16093..000000000
--- a/presentation/contrib/font-awesome-4.3.0/scss/_mixins.scss
+++ /dev/null
@@ -1,27 +0,0 @@
-// Mixins
-// --------------------------
-
-@mixin fa-icon() {
- display: inline-block;
- font: normal normal normal #{$fa-font-size-base}/1 FontAwesome; // shortening font declaration
- font-size: inherit; // can't have font-size inherit on line above, so need to override
- text-rendering: auto; // optimizelegibility throws things off #1094
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- transform: translate(0, 0); // ensures no half-pixel rendering in firefox
-
-}
-
-@mixin fa-icon-rotate($degrees, $rotation) {
- filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation});
- -webkit-transform: rotate($degrees);
- -ms-transform: rotate($degrees);
- transform: rotate($degrees);
-}
-
-@mixin fa-icon-flip($horiz, $vert, $rotation) {
- filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation});
- -webkit-transform: scale($horiz, $vert);
- -ms-transform: scale($horiz, $vert);
- transform: scale($horiz, $vert);
-}
diff --git a/presentation/contrib/font-awesome-4.3.0/scss/_path.scss b/presentation/contrib/font-awesome-4.3.0/scss/_path.scss
deleted file mode 100644
index bb457c23a..000000000
--- a/presentation/contrib/font-awesome-4.3.0/scss/_path.scss
+++ /dev/null
@@ -1,15 +0,0 @@
-/* FONT PATH
- * -------------------------- */
-
-@font-face {
- font-family: 'FontAwesome';
- src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}');
- src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'),
- url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'),
- url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'),
- url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'),
- url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg');
-// src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
- font-weight: normal;
- font-style: normal;
-}
diff --git a/presentation/contrib/font-awesome-4.3.0/scss/_rotated-flipped.scss b/presentation/contrib/font-awesome-4.3.0/scss/_rotated-flipped.scss
deleted file mode 100644
index a3558fd09..000000000
--- a/presentation/contrib/font-awesome-4.3.0/scss/_rotated-flipped.scss
+++ /dev/null
@@ -1,20 +0,0 @@
-// Rotated & Flipped Icons
-// -------------------------
-
-.#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); }
-.#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); }
-.#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); }
-
-.#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); }
-.#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); }
-
-// Hook for IE8-9
-// -------------------------
-
-:root .#{$fa-css-prefix}-rotate-90,
-:root .#{$fa-css-prefix}-rotate-180,
-:root .#{$fa-css-prefix}-rotate-270,
-:root .#{$fa-css-prefix}-flip-horizontal,
-:root .#{$fa-css-prefix}-flip-vertical {
- filter: none;
-}
diff --git a/presentation/contrib/font-awesome-4.3.0/scss/_stacked.scss b/presentation/contrib/font-awesome-4.3.0/scss/_stacked.scss
deleted file mode 100644
index aef740366..000000000
--- a/presentation/contrib/font-awesome-4.3.0/scss/_stacked.scss
+++ /dev/null
@@ -1,20 +0,0 @@
-// Stacked Icons
-// -------------------------
-
-.#{$fa-css-prefix}-stack {
- position: relative;
- display: inline-block;
- width: 2em;
- height: 2em;
- line-height: 2em;
- vertical-align: middle;
-}
-.#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x {
- position: absolute;
- left: 0;
- width: 100%;
- text-align: center;
-}
-.#{$fa-css-prefix}-stack-1x { line-height: inherit; }
-.#{$fa-css-prefix}-stack-2x { font-size: 2em; }
-.#{$fa-css-prefix}-inverse { color: $fa-inverse; }
diff --git a/presentation/contrib/font-awesome-4.3.0/scss/_variables.scss b/presentation/contrib/font-awesome-4.3.0/scss/_variables.scss
deleted file mode 100644
index 9b7210e23..000000000
--- a/presentation/contrib/font-awesome-4.3.0/scss/_variables.scss
+++ /dev/null
@@ -1,606 +0,0 @@
-// Variables
-// --------------------------
-
-$fa-font-path: "../fonts" !default;
-$fa-font-size-base: 14px !default;
-//$fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.3.0/fonts" !default; // for referencing Bootstrap CDN font files directly
-$fa-css-prefix: fa !default;
-$fa-version: "4.3.0" !default;
-$fa-border-color: #eee !default;
-$fa-inverse: #fff !default;
-$fa-li-width: (30em / 14) !default;
-
-$fa-var-adjust: "\f042";
-$fa-var-adn: "\f170";
-$fa-var-align-center: "\f037";
-$fa-var-align-justify: "\f039";
-$fa-var-align-left: "\f036";
-$fa-var-align-right: "\f038";
-$fa-var-ambulance: "\f0f9";
-$fa-var-anchor: "\f13d";
-$fa-var-android: "\f17b";
-$fa-var-angellist: "\f209";
-$fa-var-angle-double-down: "\f103";
-$fa-var-angle-double-left: "\f100";
-$fa-var-angle-double-right: "\f101";
-$fa-var-angle-double-up: "\f102";
-$fa-var-angle-down: "\f107";
-$fa-var-angle-left: "\f104";
-$fa-var-angle-right: "\f105";
-$fa-var-angle-up: "\f106";
-$fa-var-apple: "\f179";
-$fa-var-archive: "\f187";
-$fa-var-area-chart: "\f1fe";
-$fa-var-arrow-circle-down: "\f0ab";
-$fa-var-arrow-circle-left: "\f0a8";
-$fa-var-arrow-circle-o-down: "\f01a";
-$fa-var-arrow-circle-o-left: "\f190";
-$fa-var-arrow-circle-o-right: "\f18e";
-$fa-var-arrow-circle-o-up: "\f01b";
-$fa-var-arrow-circle-right: "\f0a9";
-$fa-var-arrow-circle-up: "\f0aa";
-$fa-var-arrow-down: "\f063";
-$fa-var-arrow-left: "\f060";
-$fa-var-arrow-right: "\f061";
-$fa-var-arrow-up: "\f062";
-$fa-var-arrows: "\f047";
-$fa-var-arrows-alt: "\f0b2";
-$fa-var-arrows-h: "\f07e";
-$fa-var-arrows-v: "\f07d";
-$fa-var-asterisk: "\f069";
-$fa-var-at: "\f1fa";
-$fa-var-automobile: "\f1b9";
-$fa-var-backward: "\f04a";
-$fa-var-ban: "\f05e";
-$fa-var-bank: "\f19c";
-$fa-var-bar-chart: "\f080";
-$fa-var-bar-chart-o: "\f080";
-$fa-var-barcode: "\f02a";
-$fa-var-bars: "\f0c9";
-$fa-var-bed: "\f236";
-$fa-var-beer: "\f0fc";
-$fa-var-behance: "\f1b4";
-$fa-var-behance-square: "\f1b5";
-$fa-var-bell: "\f0f3";
-$fa-var-bell-o: "\f0a2";
-$fa-var-bell-slash: "\f1f6";
-$fa-var-bell-slash-o: "\f1f7";
-$fa-var-bicycle: "\f206";
-$fa-var-binoculars: "\f1e5";
-$fa-var-birthday-cake: "\f1fd";
-$fa-var-bitbucket: "\f171";
-$fa-var-bitbucket-square: "\f172";
-$fa-var-bitcoin: "\f15a";
-$fa-var-bold: "\f032";
-$fa-var-bolt: "\f0e7";
-$fa-var-bomb: "\f1e2";
-$fa-var-book: "\f02d";
-$fa-var-bookmark: "\f02e";
-$fa-var-bookmark-o: "\f097";
-$fa-var-briefcase: "\f0b1";
-$fa-var-btc: "\f15a";
-$fa-var-bug: "\f188";
-$fa-var-building: "\f1ad";
-$fa-var-building-o: "\f0f7";
-$fa-var-bullhorn: "\f0a1";
-$fa-var-bullseye: "\f140";
-$fa-var-bus: "\f207";
-$fa-var-buysellads: "\f20d";
-$fa-var-cab: "\f1ba";
-$fa-var-calculator: "\f1ec";
-$fa-var-calendar: "\f073";
-$fa-var-calendar-o: "\f133";
-$fa-var-camera: "\f030";
-$fa-var-camera-retro: "\f083";
-$fa-var-car: "\f1b9";
-$fa-var-caret-down: "\f0d7";
-$fa-var-caret-left: "\f0d9";
-$fa-var-caret-right: "\f0da";
-$fa-var-caret-square-o-down: "\f150";
-$fa-var-caret-square-o-left: "\f191";
-$fa-var-caret-square-o-right: "\f152";
-$fa-var-caret-square-o-up: "\f151";
-$fa-var-caret-up: "\f0d8";
-$fa-var-cart-arrow-down: "\f218";
-$fa-var-cart-plus: "\f217";
-$fa-var-cc: "\f20a";
-$fa-var-cc-amex: "\f1f3";
-$fa-var-cc-discover: "\f1f2";
-$fa-var-cc-mastercard: "\f1f1";
-$fa-var-cc-paypal: "\f1f4";
-$fa-var-cc-stripe: "\f1f5";
-$fa-var-cc-visa: "\f1f0";
-$fa-var-certificate: "\f0a3";
-$fa-var-chain: "\f0c1";
-$fa-var-chain-broken: "\f127";
-$fa-var-check: "\f00c";
-$fa-var-check-circle: "\f058";
-$fa-var-check-circle-o: "\f05d";
-$fa-var-check-square: "\f14a";
-$fa-var-check-square-o: "\f046";
-$fa-var-chevron-circle-down: "\f13a";
-$fa-var-chevron-circle-left: "\f137";
-$fa-var-chevron-circle-right: "\f138";
-$fa-var-chevron-circle-up: "\f139";
-$fa-var-chevron-down: "\f078";
-$fa-var-chevron-left: "\f053";
-$fa-var-chevron-right: "\f054";
-$fa-var-chevron-up: "\f077";
-$fa-var-child: "\f1ae";
-$fa-var-circle: "\f111";
-$fa-var-circle-o: "\f10c";
-$fa-var-circle-o-notch: "\f1ce";
-$fa-var-circle-thin: "\f1db";
-$fa-var-clipboard: "\f0ea";
-$fa-var-clock-o: "\f017";
-$fa-var-close: "\f00d";
-$fa-var-cloud: "\f0c2";
-$fa-var-cloud-download: "\f0ed";
-$fa-var-cloud-upload: "\f0ee";
-$fa-var-cny: "\f157";
-$fa-var-code: "\f121";
-$fa-var-code-fork: "\f126";
-$fa-var-codepen: "\f1cb";
-$fa-var-coffee: "\f0f4";
-$fa-var-cog: "\f013";
-$fa-var-cogs: "\f085";
-$fa-var-columns: "\f0db";
-$fa-var-comment: "\f075";
-$fa-var-comment-o: "\f0e5";
-$fa-var-comments: "\f086";
-$fa-var-comments-o: "\f0e6";
-$fa-var-compass: "\f14e";
-$fa-var-compress: "\f066";
-$fa-var-connectdevelop: "\f20e";
-$fa-var-copy: "\f0c5";
-$fa-var-copyright: "\f1f9";
-$fa-var-credit-card: "\f09d";
-$fa-var-crop: "\f125";
-$fa-var-crosshairs: "\f05b";
-$fa-var-css3: "\f13c";
-$fa-var-cube: "\f1b2";
-$fa-var-cubes: "\f1b3";
-$fa-var-cut: "\f0c4";
-$fa-var-cutlery: "\f0f5";
-$fa-var-dashboard: "\f0e4";
-$fa-var-dashcube: "\f210";
-$fa-var-database: "\f1c0";
-$fa-var-dedent: "\f03b";
-$fa-var-delicious: "\f1a5";
-$fa-var-desktop: "\f108";
-$fa-var-deviantart: "\f1bd";
-$fa-var-diamond: "\f219";
-$fa-var-digg: "\f1a6";
-$fa-var-dollar: "\f155";
-$fa-var-dot-circle-o: "\f192";
-$fa-var-download: "\f019";
-$fa-var-dribbble: "\f17d";
-$fa-var-dropbox: "\f16b";
-$fa-var-drupal: "\f1a9";
-$fa-var-edit: "\f044";
-$fa-var-eject: "\f052";
-$fa-var-ellipsis-h: "\f141";
-$fa-var-ellipsis-v: "\f142";
-$fa-var-empire: "\f1d1";
-$fa-var-envelope: "\f0e0";
-$fa-var-envelope-o: "\f003";
-$fa-var-envelope-square: "\f199";
-$fa-var-eraser: "\f12d";
-$fa-var-eur: "\f153";
-$fa-var-euro: "\f153";
-$fa-var-exchange: "\f0ec";
-$fa-var-exclamation: "\f12a";
-$fa-var-exclamation-circle: "\f06a";
-$fa-var-exclamation-triangle: "\f071";
-$fa-var-expand: "\f065";
-$fa-var-external-link: "\f08e";
-$fa-var-external-link-square: "\f14c";
-$fa-var-eye: "\f06e";
-$fa-var-eye-slash: "\f070";
-$fa-var-eyedropper: "\f1fb";
-$fa-var-facebook: "\f09a";
-$fa-var-facebook-f: "\f09a";
-$fa-var-facebook-official: "\f230";
-$fa-var-facebook-square: "\f082";
-$fa-var-fast-backward: "\f049";
-$fa-var-fast-forward: "\f050";
-$fa-var-fax: "\f1ac";
-$fa-var-female: "\f182";
-$fa-var-fighter-jet: "\f0fb";
-$fa-var-file: "\f15b";
-$fa-var-file-archive-o: "\f1c6";
-$fa-var-file-audio-o: "\f1c7";
-$fa-var-file-code-o: "\f1c9";
-$fa-var-file-excel-o: "\f1c3";
-$fa-var-file-image-o: "\f1c5";
-$fa-var-file-movie-o: "\f1c8";
-$fa-var-file-o: "\f016";
-$fa-var-file-pdf-o: "\f1c1";
-$fa-var-file-photo-o: "\f1c5";
-$fa-var-file-picture-o: "\f1c5";
-$fa-var-file-powerpoint-o: "\f1c4";
-$fa-var-file-sound-o: "\f1c7";
-$fa-var-file-text: "\f15c";
-$fa-var-file-text-o: "\f0f6";
-$fa-var-file-video-o: "\f1c8";
-$fa-var-file-word-o: "\f1c2";
-$fa-var-file-zip-o: "\f1c6";
-$fa-var-files-o: "\f0c5";
-$fa-var-film: "\f008";
-$fa-var-filter: "\f0b0";
-$fa-var-fire: "\f06d";
-$fa-var-fire-extinguisher: "\f134";
-$fa-var-flag: "\f024";
-$fa-var-flag-checkered: "\f11e";
-$fa-var-flag-o: "\f11d";
-$fa-var-flash: "\f0e7";
-$fa-var-flask: "\f0c3";
-$fa-var-flickr: "\f16e";
-$fa-var-floppy-o: "\f0c7";
-$fa-var-folder: "\f07b";
-$fa-var-folder-o: "\f114";
-$fa-var-folder-open: "\f07c";
-$fa-var-folder-open-o: "\f115";
-$fa-var-font: "\f031";
-$fa-var-forumbee: "\f211";
-$fa-var-forward: "\f04e";
-$fa-var-foursquare: "\f180";
-$fa-var-frown-o: "\f119";
-$fa-var-futbol-o: "\f1e3";
-$fa-var-gamepad: "\f11b";
-$fa-var-gavel: "\f0e3";
-$fa-var-gbp: "\f154";
-$fa-var-ge: "\f1d1";
-$fa-var-gear: "\f013";
-$fa-var-gears: "\f085";
-$fa-var-genderless: "\f1db";
-$fa-var-gift: "\f06b";
-$fa-var-git: "\f1d3";
-$fa-var-git-square: "\f1d2";
-$fa-var-github: "\f09b";
-$fa-var-github-alt: "\f113";
-$fa-var-github-square: "\f092";
-$fa-var-gittip: "\f184";
-$fa-var-glass: "\f000";
-$fa-var-globe: "\f0ac";
-$fa-var-google: "\f1a0";
-$fa-var-google-plus: "\f0d5";
-$fa-var-google-plus-square: "\f0d4";
-$fa-var-google-wallet: "\f1ee";
-$fa-var-graduation-cap: "\f19d";
-$fa-var-gratipay: "\f184";
-$fa-var-group: "\f0c0";
-$fa-var-h-square: "\f0fd";
-$fa-var-hacker-news: "\f1d4";
-$fa-var-hand-o-down: "\f0a7";
-$fa-var-hand-o-left: "\f0a5";
-$fa-var-hand-o-right: "\f0a4";
-$fa-var-hand-o-up: "\f0a6";
-$fa-var-hdd-o: "\f0a0";
-$fa-var-header: "\f1dc";
-$fa-var-headphones: "\f025";
-$fa-var-heart: "\f004";
-$fa-var-heart-o: "\f08a";
-$fa-var-heartbeat: "\f21e";
-$fa-var-history: "\f1da";
-$fa-var-home: "\f015";
-$fa-var-hospital-o: "\f0f8";
-$fa-var-hotel: "\f236";
-$fa-var-html5: "\f13b";
-$fa-var-ils: "\f20b";
-$fa-var-image: "\f03e";
-$fa-var-inbox: "\f01c";
-$fa-var-indent: "\f03c";
-$fa-var-info: "\f129";
-$fa-var-info-circle: "\f05a";
-$fa-var-inr: "\f156";
-$fa-var-instagram: "\f16d";
-$fa-var-institution: "\f19c";
-$fa-var-ioxhost: "\f208";
-$fa-var-italic: "\f033";
-$fa-var-joomla: "\f1aa";
-$fa-var-jpy: "\f157";
-$fa-var-jsfiddle: "\f1cc";
-$fa-var-key: "\f084";
-$fa-var-keyboard-o: "\f11c";
-$fa-var-krw: "\f159";
-$fa-var-language: "\f1ab";
-$fa-var-laptop: "\f109";
-$fa-var-lastfm: "\f202";
-$fa-var-lastfm-square: "\f203";
-$fa-var-leaf: "\f06c";
-$fa-var-leanpub: "\f212";
-$fa-var-legal: "\f0e3";
-$fa-var-lemon-o: "\f094";
-$fa-var-level-down: "\f149";
-$fa-var-level-up: "\f148";
-$fa-var-life-bouy: "\f1cd";
-$fa-var-life-buoy: "\f1cd";
-$fa-var-life-ring: "\f1cd";
-$fa-var-life-saver: "\f1cd";
-$fa-var-lightbulb-o: "\f0eb";
-$fa-var-line-chart: "\f201";
-$fa-var-link: "\f0c1";
-$fa-var-linkedin: "\f0e1";
-$fa-var-linkedin-square: "\f08c";
-$fa-var-linux: "\f17c";
-$fa-var-list: "\f03a";
-$fa-var-list-alt: "\f022";
-$fa-var-list-ol: "\f0cb";
-$fa-var-list-ul: "\f0ca";
-$fa-var-location-arrow: "\f124";
-$fa-var-lock: "\f023";
-$fa-var-long-arrow-down: "\f175";
-$fa-var-long-arrow-left: "\f177";
-$fa-var-long-arrow-right: "\f178";
-$fa-var-long-arrow-up: "\f176";
-$fa-var-magic: "\f0d0";
-$fa-var-magnet: "\f076";
-$fa-var-mail-forward: "\f064";
-$fa-var-mail-reply: "\f112";
-$fa-var-mail-reply-all: "\f122";
-$fa-var-male: "\f183";
-$fa-var-map-marker: "\f041";
-$fa-var-mars: "\f222";
-$fa-var-mars-double: "\f227";
-$fa-var-mars-stroke: "\f229";
-$fa-var-mars-stroke-h: "\f22b";
-$fa-var-mars-stroke-v: "\f22a";
-$fa-var-maxcdn: "\f136";
-$fa-var-meanpath: "\f20c";
-$fa-var-medium: "\f23a";
-$fa-var-medkit: "\f0fa";
-$fa-var-meh-o: "\f11a";
-$fa-var-mercury: "\f223";
-$fa-var-microphone: "\f130";
-$fa-var-microphone-slash: "\f131";
-$fa-var-minus: "\f068";
-$fa-var-minus-circle: "\f056";
-$fa-var-minus-square: "\f146";
-$fa-var-minus-square-o: "\f147";
-$fa-var-mobile: "\f10b";
-$fa-var-mobile-phone: "\f10b";
-$fa-var-money: "\f0d6";
-$fa-var-moon-o: "\f186";
-$fa-var-mortar-board: "\f19d";
-$fa-var-motorcycle: "\f21c";
-$fa-var-music: "\f001";
-$fa-var-navicon: "\f0c9";
-$fa-var-neuter: "\f22c";
-$fa-var-newspaper-o: "\f1ea";
-$fa-var-openid: "\f19b";
-$fa-var-outdent: "\f03b";
-$fa-var-pagelines: "\f18c";
-$fa-var-paint-brush: "\f1fc";
-$fa-var-paper-plane: "\f1d8";
-$fa-var-paper-plane-o: "\f1d9";
-$fa-var-paperclip: "\f0c6";
-$fa-var-paragraph: "\f1dd";
-$fa-var-paste: "\f0ea";
-$fa-var-pause: "\f04c";
-$fa-var-paw: "\f1b0";
-$fa-var-paypal: "\f1ed";
-$fa-var-pencil: "\f040";
-$fa-var-pencil-square: "\f14b";
-$fa-var-pencil-square-o: "\f044";
-$fa-var-phone: "\f095";
-$fa-var-phone-square: "\f098";
-$fa-var-photo: "\f03e";
-$fa-var-picture-o: "\f03e";
-$fa-var-pie-chart: "\f200";
-$fa-var-pied-piper: "\f1a7";
-$fa-var-pied-piper-alt: "\f1a8";
-$fa-var-pinterest: "\f0d2";
-$fa-var-pinterest-p: "\f231";
-$fa-var-pinterest-square: "\f0d3";
-$fa-var-plane: "\f072";
-$fa-var-play: "\f04b";
-$fa-var-play-circle: "\f144";
-$fa-var-play-circle-o: "\f01d";
-$fa-var-plug: "\f1e6";
-$fa-var-plus: "\f067";
-$fa-var-plus-circle: "\f055";
-$fa-var-plus-square: "\f0fe";
-$fa-var-plus-square-o: "\f196";
-$fa-var-power-off: "\f011";
-$fa-var-print: "\f02f";
-$fa-var-puzzle-piece: "\f12e";
-$fa-var-qq: "\f1d6";
-$fa-var-qrcode: "\f029";
-$fa-var-question: "\f128";
-$fa-var-question-circle: "\f059";
-$fa-var-quote-left: "\f10d";
-$fa-var-quote-right: "\f10e";
-$fa-var-ra: "\f1d0";
-$fa-var-random: "\f074";
-$fa-var-rebel: "\f1d0";
-$fa-var-recycle: "\f1b8";
-$fa-var-reddit: "\f1a1";
-$fa-var-reddit-square: "\f1a2";
-$fa-var-refresh: "\f021";
-$fa-var-remove: "\f00d";
-$fa-var-renren: "\f18b";
-$fa-var-reorder: "\f0c9";
-$fa-var-repeat: "\f01e";
-$fa-var-reply: "\f112";
-$fa-var-reply-all: "\f122";
-$fa-var-retweet: "\f079";
-$fa-var-rmb: "\f157";
-$fa-var-road: "\f018";
-$fa-var-rocket: "\f135";
-$fa-var-rotate-left: "\f0e2";
-$fa-var-rotate-right: "\f01e";
-$fa-var-rouble: "\f158";
-$fa-var-rss: "\f09e";
-$fa-var-rss-square: "\f143";
-$fa-var-rub: "\f158";
-$fa-var-ruble: "\f158";
-$fa-var-rupee: "\f156";
-$fa-var-save: "\f0c7";
-$fa-var-scissors: "\f0c4";
-$fa-var-search: "\f002";
-$fa-var-search-minus: "\f010";
-$fa-var-search-plus: "\f00e";
-$fa-var-sellsy: "\f213";
-$fa-var-send: "\f1d8";
-$fa-var-send-o: "\f1d9";
-$fa-var-server: "\f233";
-$fa-var-share: "\f064";
-$fa-var-share-alt: "\f1e0";
-$fa-var-share-alt-square: "\f1e1";
-$fa-var-share-square: "\f14d";
-$fa-var-share-square-o: "\f045";
-$fa-var-shekel: "\f20b";
-$fa-var-sheqel: "\f20b";
-$fa-var-shield: "\f132";
-$fa-var-ship: "\f21a";
-$fa-var-shirtsinbulk: "\f214";
-$fa-var-shopping-cart: "\f07a";
-$fa-var-sign-in: "\f090";
-$fa-var-sign-out: "\f08b";
-$fa-var-signal: "\f012";
-$fa-var-simplybuilt: "\f215";
-$fa-var-sitemap: "\f0e8";
-$fa-var-skyatlas: "\f216";
-$fa-var-skype: "\f17e";
-$fa-var-slack: "\f198";
-$fa-var-sliders: "\f1de";
-$fa-var-slideshare: "\f1e7";
-$fa-var-smile-o: "\f118";
-$fa-var-soccer-ball-o: "\f1e3";
-$fa-var-sort: "\f0dc";
-$fa-var-sort-alpha-asc: "\f15d";
-$fa-var-sort-alpha-desc: "\f15e";
-$fa-var-sort-amount-asc: "\f160";
-$fa-var-sort-amount-desc: "\f161";
-$fa-var-sort-asc: "\f0de";
-$fa-var-sort-desc: "\f0dd";
-$fa-var-sort-down: "\f0dd";
-$fa-var-sort-numeric-asc: "\f162";
-$fa-var-sort-numeric-desc: "\f163";
-$fa-var-sort-up: "\f0de";
-$fa-var-soundcloud: "\f1be";
-$fa-var-space-shuttle: "\f197";
-$fa-var-spinner: "\f110";
-$fa-var-spoon: "\f1b1";
-$fa-var-spotify: "\f1bc";
-$fa-var-square: "\f0c8";
-$fa-var-square-o: "\f096";
-$fa-var-stack-exchange: "\f18d";
-$fa-var-stack-overflow: "\f16c";
-$fa-var-star: "\f005";
-$fa-var-star-half: "\f089";
-$fa-var-star-half-empty: "\f123";
-$fa-var-star-half-full: "\f123";
-$fa-var-star-half-o: "\f123";
-$fa-var-star-o: "\f006";
-$fa-var-steam: "\f1b6";
-$fa-var-steam-square: "\f1b7";
-$fa-var-step-backward: "\f048";
-$fa-var-step-forward: "\f051";
-$fa-var-stethoscope: "\f0f1";
-$fa-var-stop: "\f04d";
-$fa-var-street-view: "\f21d";
-$fa-var-strikethrough: "\f0cc";
-$fa-var-stumbleupon: "\f1a4";
-$fa-var-stumbleupon-circle: "\f1a3";
-$fa-var-subscript: "\f12c";
-$fa-var-subway: "\f239";
-$fa-var-suitcase: "\f0f2";
-$fa-var-sun-o: "\f185";
-$fa-var-superscript: "\f12b";
-$fa-var-support: "\f1cd";
-$fa-var-table: "\f0ce";
-$fa-var-tablet: "\f10a";
-$fa-var-tachometer: "\f0e4";
-$fa-var-tag: "\f02b";
-$fa-var-tags: "\f02c";
-$fa-var-tasks: "\f0ae";
-$fa-var-taxi: "\f1ba";
-$fa-var-tencent-weibo: "\f1d5";
-$fa-var-terminal: "\f120";
-$fa-var-text-height: "\f034";
-$fa-var-text-width: "\f035";
-$fa-var-th: "\f00a";
-$fa-var-th-large: "\f009";
-$fa-var-th-list: "\f00b";
-$fa-var-thumb-tack: "\f08d";
-$fa-var-thumbs-down: "\f165";
-$fa-var-thumbs-o-down: "\f088";
-$fa-var-thumbs-o-up: "\f087";
-$fa-var-thumbs-up: "\f164";
-$fa-var-ticket: "\f145";
-$fa-var-times: "\f00d";
-$fa-var-times-circle: "\f057";
-$fa-var-times-circle-o: "\f05c";
-$fa-var-tint: "\f043";
-$fa-var-toggle-down: "\f150";
-$fa-var-toggle-left: "\f191";
-$fa-var-toggle-off: "\f204";
-$fa-var-toggle-on: "\f205";
-$fa-var-toggle-right: "\f152";
-$fa-var-toggle-up: "\f151";
-$fa-var-train: "\f238";
-$fa-var-transgender: "\f224";
-$fa-var-transgender-alt: "\f225";
-$fa-var-trash: "\f1f8";
-$fa-var-trash-o: "\f014";
-$fa-var-tree: "\f1bb";
-$fa-var-trello: "\f181";
-$fa-var-trophy: "\f091";
-$fa-var-truck: "\f0d1";
-$fa-var-try: "\f195";
-$fa-var-tty: "\f1e4";
-$fa-var-tumblr: "\f173";
-$fa-var-tumblr-square: "\f174";
-$fa-var-turkish-lira: "\f195";
-$fa-var-twitch: "\f1e8";
-$fa-var-twitter: "\f099";
-$fa-var-twitter-square: "\f081";
-$fa-var-umbrella: "\f0e9";
-$fa-var-underline: "\f0cd";
-$fa-var-undo: "\f0e2";
-$fa-var-university: "\f19c";
-$fa-var-unlink: "\f127";
-$fa-var-unlock: "\f09c";
-$fa-var-unlock-alt: "\f13e";
-$fa-var-unsorted: "\f0dc";
-$fa-var-upload: "\f093";
-$fa-var-usd: "\f155";
-$fa-var-user: "\f007";
-$fa-var-user-md: "\f0f0";
-$fa-var-user-plus: "\f234";
-$fa-var-user-secret: "\f21b";
-$fa-var-user-times: "\f235";
-$fa-var-users: "\f0c0";
-$fa-var-venus: "\f221";
-$fa-var-venus-double: "\f226";
-$fa-var-venus-mars: "\f228";
-$fa-var-viacoin: "\f237";
-$fa-var-video-camera: "\f03d";
-$fa-var-vimeo-square: "\f194";
-$fa-var-vine: "\f1ca";
-$fa-var-vk: "\f189";
-$fa-var-volume-down: "\f027";
-$fa-var-volume-off: "\f026";
-$fa-var-volume-up: "\f028";
-$fa-var-warning: "\f071";
-$fa-var-wechat: "\f1d7";
-$fa-var-weibo: "\f18a";
-$fa-var-weixin: "\f1d7";
-$fa-var-whatsapp: "\f232";
-$fa-var-wheelchair: "\f193";
-$fa-var-wifi: "\f1eb";
-$fa-var-windows: "\f17a";
-$fa-var-won: "\f159";
-$fa-var-wordpress: "\f19a";
-$fa-var-wrench: "\f0ad";
-$fa-var-xing: "\f168";
-$fa-var-xing-square: "\f169";
-$fa-var-yahoo: "\f19e";
-$fa-var-yelp: "\f1e9";
-$fa-var-yen: "\f157";
-$fa-var-youtube: "\f167";
-$fa-var-youtube-play: "\f16a";
-$fa-var-youtube-square: "\f166";
-
diff --git a/presentation/contrib/font-awesome-4.3.0/scss/font-awesome.scss b/presentation/contrib/font-awesome-4.3.0/scss/font-awesome.scss
deleted file mode 100644
index 388ac6b0c..000000000
--- a/presentation/contrib/font-awesome-4.3.0/scss/font-awesome.scss
+++ /dev/null
@@ -1,17 +0,0 @@
-/*!
- * Font Awesome 4.3.0 by @davegandy - http://fontawesome.io - @fontawesome
- * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
- */
-
-@import "variables";
-@import "mixins";
-@import "path";
-@import "core";
-@import "larger";
-@import "fixed-width";
-@import "list";
-@import "bordered-pulled";
-@import "animated";
-@import "rotated-flipped";
-@import "stacked";
-@import "icons";
diff --git a/presentation/contrib/google/css/lato.css b/presentation/contrib/google/css/lato.css
deleted file mode 100644
index 08c845180..000000000
--- a/presentation/contrib/google/css/lato.css
+++ /dev/null
@@ -1,24 +0,0 @@
-@font-face {
- font-family: 'Lato';
- font-style: normal;
- font-weight: 400;
- src: local('Lato Regular'), local('Lato-Regular'), url(../fonts/v0SdcGFAl2aezM9Vq_aFTQ.ttf) format('truetype');
-}
-@font-face {
- font-family: 'Lato';
- font-style: normal;
- font-weight: 700;
- src: local('Lato Bold'), local('Lato-Bold'), url(../fonts/DvlFBScY1r-FMtZSYIYoYw.ttf) format('truetype');
-}
-@font-face {
- font-family: 'Lato';
- font-style: italic;
- font-weight: 400;
- src: local('Lato Italic'), local('Lato-Italic'), url(../fonts/LqowQDslGv4DmUBAfWa2Vw.ttf) format('truetype');
-}
-@font-face {
- font-family: 'Lato';
- font-style: italic;
- font-weight: 700;
- src: local('Lato Bold Italic'), local('Lato-BoldItalic'), url(../fonts/HkF_qI1x_noxlxhrhMQYEKCWcynf_cDxXwCLxiixG1c.ttf) format('truetype');
-}
diff --git a/presentation/contrib/google/fonts/DvlFBScY1r-FMtZSYIYoYw.ttf b/presentation/contrib/google/fonts/DvlFBScY1r-FMtZSYIYoYw.ttf
deleted file mode 100644
index e8b9bf6a2..000000000
Binary files a/presentation/contrib/google/fonts/DvlFBScY1r-FMtZSYIYoYw.ttf and /dev/null differ
diff --git a/presentation/contrib/google/fonts/HkF_qI1x_noxlxhrhMQYEKCWcynf_cDxXwCLxiixG1c.ttf b/presentation/contrib/google/fonts/HkF_qI1x_noxlxhrhMQYEKCWcynf_cDxXwCLxiixG1c.ttf
deleted file mode 100644
index 608830e99..000000000
Binary files a/presentation/contrib/google/fonts/HkF_qI1x_noxlxhrhMQYEKCWcynf_cDxXwCLxiixG1c.ttf and /dev/null differ
diff --git a/presentation/contrib/google/fonts/LqowQDslGv4DmUBAfWa2Vw.ttf b/presentation/contrib/google/fonts/LqowQDslGv4DmUBAfWa2Vw.ttf
deleted file mode 100644
index 7c8319852..000000000
Binary files a/presentation/contrib/google/fonts/LqowQDslGv4DmUBAfWa2Vw.ttf and /dev/null differ
diff --git a/presentation/contrib/google/fonts/v0SdcGFAl2aezM9Vq_aFTQ.ttf b/presentation/contrib/google/fonts/v0SdcGFAl2aezM9Vq_aFTQ.ttf
deleted file mode 100644
index 7608bc3e0..000000000
Binary files a/presentation/contrib/google/fonts/v0SdcGFAl2aezM9Vq_aFTQ.ttf and /dev/null differ
diff --git a/presentation/css/print/paper.css b/presentation/css/print/paper.css
deleted file mode 100644
index 7c7257ae4..000000000
--- a/presentation/css/print/paper.css
+++ /dev/null
@@ -1,202 +0,0 @@
-/* Default Print Stylesheet Template
- by Rob Glazebrook of CSSnewbie.com
- Last Updated: June 4, 2008
-
- Feel free (nay, compelled) to edit, append, and
- manipulate this file as you see fit. */
-
-
-@media print {
-
- /* SECTION 1: Set default width, margin, float, and
- background. This prevents elements from extending
- beyond the edge of the printed page, and prevents
- unnecessary background images from printing */
- html {
- background: #fff;
- width: auto;
- height: auto;
- overflow: visible;
- }
- body {
- background: #fff;
- font-size: 20pt;
- width: auto;
- height: auto;
- border: 0;
- margin: 0 5%;
- padding: 0;
- overflow: visible;
- float: none !important;
- }
-
- /* SECTION 2: Remove any elements not needed in print.
- This would include navigation, ads, sidebars, etc. */
- .nestedarrow,
- .controls,
- .fork-reveal,
- .share-reveal,
- .state-background,
- .reveal .progress,
- .reveal .backgrounds {
- display: none !important;
- }
-
- /* SECTION 3: Set body font face, size, and color.
- Consider using a serif font for readability. */
- body, p, td, li, div {
- font-size: 20pt!important;
- font-family: Georgia, "Times New Roman", Times, serif !important;
- color: #000;
- }
-
- /* SECTION 4: Set heading font face, sizes, and color.
- Differentiate your headings from your body text.
- Perhaps use a large sans-serif for distinction. */
- h1,h2,h3,h4,h5,h6 {
- color: #000!important;
- height: auto;
- line-height: normal;
- font-family: Georgia, "Times New Roman", Times, serif !important;
- text-shadow: 0 0 0 #000 !important;
- text-align: left;
- letter-spacing: normal;
- }
- /* Need to reduce the size of the fonts for printing */
- h1 { font-size: 28pt !important; }
- h2 { font-size: 24pt !important; }
- h3 { font-size: 22pt !important; }
- h4 { font-size: 22pt !important; font-variant: small-caps; }
- h5 { font-size: 21pt !important; }
- h6 { font-size: 20pt !important; font-style: italic; }
-
- /* SECTION 5: Make hyperlinks more usable.
- Ensure links are underlined, and consider appending
- the URL to the end of the link for usability. */
- a:link,
- a:visited {
- color: #000 !important;
- font-weight: bold;
- text-decoration: underline;
- }
- /*
- .reveal a:link:after,
- .reveal a:visited:after {
- content: " (" attr(href) ") ";
- color: #222 !important;
- font-size: 90%;
- }
- */
-
-
- /* SECTION 6: more reveal.js specific additions by @skypanther */
- ul, ol, div, p {
- visibility: visible;
- position: static;
- width: auto;
- height: auto;
- display: block;
- overflow: visible;
- margin: 0;
- text-align: left !important;
- }
- .reveal pre,
- .reveal table {
- margin-left: 0;
- margin-right: 0;
- }
- .reveal pre code {
- padding: 20px;
- border: 1px solid #ddd;
- }
- .reveal blockquote {
- margin: 20px 0;
- }
- .reveal .slides {
- position: static !important;
- width: auto !important;
- height: auto !important;
-
- left: 0 !important;
- top: 0 !important;
- margin-left: 0 !important;
- margin-top: 0 !important;
- padding: 0 !important;
- zoom: 1 !important;
-
- overflow: visible !important;
- display: block !important;
-
- text-align: left !important;
- -webkit-perspective: none;
- -moz-perspective: none;
- -ms-perspective: none;
- perspective: none;
-
- -webkit-perspective-origin: 50% 50%;
- -moz-perspective-origin: 50% 50%;
- -ms-perspective-origin: 50% 50%;
- perspective-origin: 50% 50%;
- }
- .reveal .slides section {
- visibility: visible !important;
- position: static !important;
- width: 100% !important;
- height: auto !important;
- display: block !important;
- overflow: visible !important;
-
- left: 0 !important;
- top: 0 !important;
- margin-left: 0 !important;
- margin-top: 0 !important;
- padding: 60px 20px !important;
- z-index: auto !important;
-
- opacity: 1 !important;
-
- page-break-after: always !important;
-
- -webkit-transform-style: flat !important;
- -moz-transform-style: flat !important;
- -ms-transform-style: flat !important;
- transform-style: flat !important;
-
- -webkit-transform: none !important;
- -moz-transform: none !important;
- -ms-transform: none !important;
- transform: none !important;
-
- -webkit-transition: none !important;
- -moz-transition: none !important;
- -ms-transition: none !important;
- transition: none !important;
- }
- .reveal .slides section.stack {
- padding: 0 !important;
- }
- .reveal section:last-of-type {
- page-break-after: avoid !important;
- }
- .reveal section .fragment {
- opacity: 1 !important;
- visibility: visible !important;
-
- -webkit-transform: none !important;
- -moz-transform: none !important;
- -ms-transform: none !important;
- transform: none !important;
- }
- .reveal section img {
- display: block;
- margin: 15px 0px;
- background: rgba(255,255,255,1);
- border: 1px solid #666;
- box-shadow: none;
- }
-
- .reveal section small {
- font-size: 0.8em;
- }
-
-}
\ No newline at end of file
diff --git a/presentation/css/print/pdf.css b/presentation/css/print/pdf.css
deleted file mode 100644
index 2eb4cf23d..000000000
--- a/presentation/css/print/pdf.css
+++ /dev/null
@@ -1,157 +0,0 @@
-/* Default Print Stylesheet Template
- by Rob Glazebrook of CSSnewbie.com
- Last Updated: June 4, 2008
-
- Feel free (nay, compelled) to edit, append, and
- manipulate this file as you see fit. */
-
-
-/* SECTION 1: Set default width, margin, float, and
- background. This prevents elements from extending
- beyond the edge of the printed page, and prevents
- unnecessary background images from printing */
-
-* {
- -webkit-print-color-adjust: exact;
-}
-
-body {
- margin: 0 auto !important;
- border: 0;
- padding: 0;
- float: none !important;
- overflow: visible;
-}
-
-html {
- width: 100%;
- height: 100%;
- overflow: visible;
-}
-
-/* SECTION 2: Remove any elements not needed in print.
- This would include navigation, ads, sidebars, etc. */
-.nestedarrow,
-.reveal .controls,
-.reveal .progress,
-.reveal .slide-number,
-.reveal .playback,
-.reveal.overview,
-.fork-reveal,
-.share-reveal,
-.state-background {
- display: none !important;
-}
-
-/* SECTION 3: Set body font face, size, and color.
- Consider using a serif font for readability. */
-body, p, td, li, div {
-
-}
-
-/* SECTION 4: Set heading font face, sizes, and color.
- Differentiate your headings from your body text.
- Perhaps use a large sans-serif for distinction. */
-h1,h2,h3,h4,h5,h6 {
- text-shadow: 0 0 0 #000 !important;
-}
-
-.reveal pre code {
- overflow: hidden !important;
- font-family: Courier, 'Courier New', monospace !important;
-}
-
-
-/* SECTION 5: more reveal.js specific additions by @skypanther */
-ul, ol, div, p {
- visibility: visible;
- position: static;
- width: auto;
- height: auto;
- display: block;
- overflow: visible;
- margin: auto;
-}
-.reveal {
- width: auto !important;
- height: auto !important;
- overflow: hidden !important;
-}
-.reveal .slides {
- position: static;
- width: 100%;
- height: auto;
-
- left: auto;
- top: auto;
- margin: 0 !important;
- padding: 0 !important;
-
- overflow: visible;
- display: block;
-
- -webkit-perspective: none;
- -moz-perspective: none;
- -ms-perspective: none;
- perspective: none;
-
- -webkit-perspective-origin: 50% 50%; /* there isn't a none/auto value but 50-50 is the default */
- -moz-perspective-origin: 50% 50%;
- -ms-perspective-origin: 50% 50%;
- perspective-origin: 50% 50%;
-}
-.reveal .slides section {
- page-break-after: always !important;
-
- visibility: visible !important;
- position: relative !important;
- display: block !important;
- position: relative !important;
-
- margin: 0 !important;
- padding: 0 !important;
- box-sizing: border-box !important;
- min-height: 1px;
-
- opacity: 1 !important;
-
- -webkit-transform-style: flat !important;
- -moz-transform-style: flat !important;
- -ms-transform-style: flat !important;
- transform-style: flat !important;
-
- -webkit-transform: none !important;
- -moz-transform: none !important;
- -ms-transform: none !important;
- transform: none !important;
-}
-.reveal section.stack {
- margin: 0 !important;
- padding: 0 !important;
- page-break-after: avoid !important;
- height: auto !important;
- min-height: auto !important;
-}
-.reveal img {
- box-shadow: none;
-}
-.reveal .roll {
- overflow: visible;
- line-height: 1em;
-}
-
-/* Slide backgrounds are placed inside of their slide when exporting to PDF */
-.reveal section .slide-background {
- display: block !important;
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- z-index: -1;
-}
-/* All elements should be above the slide-background */
-.reveal section>* {
- position: relative;
- z-index: 1;
-}
-
diff --git a/presentation/css/reveal.css b/presentation/css/reveal.css
deleted file mode 100644
index 05bfd2d1f..000000000
--- a/presentation/css/reveal.css
+++ /dev/null
@@ -1,1141 +0,0 @@
-/*!
- * reveal.js
- * http://lab.hakim.se/reveal-js
- * MIT licensed
- *
- * Copyright (C) 2015 Hakim El Hattab, http://hakim.se
- */
-/*********************************************
- * RESET STYLES
- *********************************************/
-html, body, .reveal div, .reveal span, .reveal applet, .reveal object, .reveal iframe, .reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6, .reveal p, .reveal blockquote, .reveal pre, .reveal a, .reveal abbr, .reveal acronym, .reveal address, .reveal big, .reveal cite, .reveal code, .reveal del, .reveal dfn, .reveal em, .reveal img, .reveal ins, .reveal kbd, .reveal q, .reveal s, .reveal samp, .reveal small, .reveal strike, .reveal strong, .reveal sub, .reveal sup, .reveal tt, .reveal var, .reveal b, .reveal u, .reveal center, .reveal dl, .reveal dt, .reveal dd, .reveal ol, .reveal ul, .reveal li, .reveal fieldset, .reveal form, .reveal label, .reveal legend, .reveal table, .reveal caption, .reveal tbody, .reveal tfoot, .reveal thead, .reveal tr, .reveal th, .reveal td, .reveal article, .reveal aside, .reveal canvas, .reveal details, .reveal embed, .reveal figure, .reveal figcaption, .reveal footer, .reveal header, .reveal hgroup, .reveal menu, .reveal nav, .reveal output, .reveal ruby, .reveal section, .reveal summary, .reveal time, .reveal mark, .reveal audio, video {
- margin: 0;
- padding: 0;
- border: 0;
- font-size: 100%;
- font: inherit;
- vertical-align: baseline; }
-
-.reveal article, .reveal aside, .reveal details, .reveal figcaption, .reveal figure, .reveal footer, .reveal header, .reveal hgroup, .reveal menu, .reveal nav, .reveal section {
- display: block; }
-
-/*********************************************
- * GLOBAL STYLES
- *********************************************/
-html, body {
- width: 100%;
- height: 100%;
- overflow: hidden; }
-
-body {
- position: relative;
- line-height: 1;
- background-color: #fff;
- color: #000; }
-
-::-moz-selection {
- background: #FF5E99;
- color: #fff;
- text-shadow: none; }
-
-::selection {
- background: #FF5E99;
- color: #fff;
- text-shadow: none; }
-
-/*********************************************
- * VIEW FRAGMENTS
- *********************************************/
-.reveal .slides section .fragment {
- opacity: 0;
- visibility: hidden;
- -webkit-transition: all 0.2s ease;
- transition: all 0.2s ease; }
- .reveal .slides section .fragment.visible {
- opacity: 1;
- visibility: visible; }
-
-.reveal .slides section .fragment.grow {
- opacity: 1;
- visibility: visible; }
- .reveal .slides section .fragment.grow.visible {
- -webkit-transform: scale(1.3);
- -ms-transform: scale(1.3);
- transform: scale(1.3); }
-
-.reveal .slides section .fragment.shrink {
- opacity: 1;
- visibility: visible; }
- .reveal .slides section .fragment.shrink.visible {
- -webkit-transform: scale(0.7);
- -ms-transform: scale(0.7);
- transform: scale(0.7); }
-
-.reveal .slides section .fragment.zoom-in {
- -webkit-transform: scale(0.1);
- -ms-transform: scale(0.1);
- transform: scale(0.1); }
- .reveal .slides section .fragment.zoom-in.visible {
- -webkit-transform: scale(1);
- -ms-transform: scale(1);
- transform: scale(1); }
-
-.reveal .slides section .fragment.roll-in {
- -webkit-transform: rotateX(90deg);
- transform: rotateX(90deg); }
- .reveal .slides section .fragment.roll-in.visible {
- -webkit-transform: rotateX(0);
- transform: rotateX(0); }
-
-.reveal .slides section .fragment.fade-out {
- opacity: 1;
- visibility: visible; }
- .reveal .slides section .fragment.fade-out.visible {
- opacity: 0;
- visibility: hidden; }
-
-.reveal .slides section .fragment.semi-fade-out {
- opacity: 1;
- visibility: visible; }
- .reveal .slides section .fragment.semi-fade-out.visible {
- opacity: 0.5;
- visibility: visible; }
-
-.reveal .slides section .fragment.strike {
- opacity: 1; }
- .reveal .slides section .fragment.strike.visible {
- text-decoration: line-through; }
-
-.reveal .slides section .fragment.current-visible {
- opacity: 0;
- visibility: hidden; }
- .reveal .slides section .fragment.current-visible.current-fragment {
- opacity: 1;
- visibility: visible; }
-
-.reveal .slides section .fragment.highlight-red, .reveal .slides section .fragment.highlight-current-red, .reveal .slides section .fragment.highlight-green, .reveal .slides section .fragment.highlight-current-green, .reveal .slides section .fragment.highlight-blue, .reveal .slides section .fragment.highlight-current-blue {
- opacity: 1;
- visibility: visible; }
-
-.reveal .slides section .fragment.highlight-red.visible {
- color: #ff2c2d; }
-
-.reveal .slides section .fragment.highlight-green.visible {
- color: #17ff2e; }
-
-.reveal .slides section .fragment.highlight-blue.visible {
- color: #1b91ff; }
-
-.reveal .slides section .fragment.highlight-current-red.current-fragment {
- color: #ff2c2d; }
-
-.reveal .slides section .fragment.highlight-current-green.current-fragment {
- color: #17ff2e; }
-
-.reveal .slides section .fragment.highlight-current-blue.current-fragment {
- color: #1b91ff; }
-
-/*********************************************
- * DEFAULT ELEMENT STYLES
- *********************************************/
-/* Fixes issue in Chrome where italic fonts did not appear when printing to PDF */
-.reveal:after {
- content: '';
- font-style: italic; }
-
-.reveal iframe {
- z-index: 1; }
-
-/** Prevents layering issues in certain browser/transition combinations */
-.reveal a {
- position: relative; }
-
-.reveal .stretch {
- max-width: none;
- max-height: none; }
-
-.reveal pre.stretch code {
- height: 100%;
- max-height: 100%;
- -moz-box-sizing: border-box;
- box-sizing: border-box; }
-
-/*********************************************
- * CONTROLS
- *********************************************/
-.reveal .controls {
- display: none;
- position: fixed;
- width: 110px;
- height: 110px;
- z-index: 30;
- right: 10px;
- bottom: 10px;
- -webkit-user-select: none; }
-
-.reveal .controls div {
- position: absolute;
- opacity: 0.05;
- width: 0;
- height: 0;
- border: 12px solid transparent;
- -webkit-transform: scale(0.9999);
- -ms-transform: scale(0.9999);
- transform: scale(0.9999);
- -webkit-transition: all 0.2s ease;
- transition: all 0.2s ease;
- -webkit-tap-highlight-color: rgba(0, 0, 0, 0); }
-
-.reveal .controls div.enabled {
- opacity: 0.7;
- cursor: pointer; }
-
-.reveal .controls div.enabled:active {
- margin-top: 1px; }
-
-.reveal .controls div.navigate-left {
- top: 42px;
- border-right-width: 22px;
- border-right-color: #000; }
-
-.reveal .controls div.navigate-left.fragmented {
- opacity: 0.3; }
-
-.reveal .controls div.navigate-right {
- left: 74px;
- top: 42px;
- border-left-width: 22px;
- border-left-color: #000; }
-
-.reveal .controls div.navigate-right.fragmented {
- opacity: 0.3; }
-
-.reveal .controls div.navigate-up {
- left: 42px;
- border-bottom-width: 22px;
- border-bottom-color: #000; }
-
-.reveal .controls div.navigate-up.fragmented {
- opacity: 0.3; }
-
-.reveal .controls div.navigate-down {
- left: 42px;
- top: 74px;
- border-top-width: 22px;
- border-top-color: #000; }
-
-.reveal .controls div.navigate-down.fragmented {
- opacity: 0.3; }
-
-/*********************************************
- * PROGRESS BAR
- *********************************************/
-.reveal .progress {
- position: fixed;
- display: none;
- height: 3px;
- width: 100%;
- bottom: 0;
- left: 0;
- z-index: 10;
- background-color: rgba(0, 0, 0, 0.2); }
-
-.reveal .progress:after {
- content: '';
- display: block;
- position: absolute;
- height: 20px;
- width: 100%;
- top: -20px; }
-
-.reveal .progress span {
- display: block;
- height: 100%;
- width: 0px;
- background-color: #000;
- -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
-
-/*********************************************
- * SLIDE NUMBER
- *********************************************/
-.reveal .slide-number {
- position: fixed;
- display: block;
- right: 15px;
- bottom: 15px;
- opacity: 0.5;
- z-index: 31;
- font-size: 12px; }
-
-/*********************************************
- * SLIDES
- *********************************************/
-.reveal {
- position: relative;
- width: 100%;
- height: 100%;
- -ms-touch-action: none;
- touch-action: none; }
-
-.reveal .slides {
- position: absolute;
- width: 100%;
- height: 100%;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- margin: auto;
- overflow: visible;
- z-index: 1;
- text-align: center;
- -webkit-perspective: 600px;
- perspective: 600px;
- -webkit-perspective-origin: 50% 40%;
- perspective-origin: 50% 40%; }
-
-.reveal .slides > section {
- -ms-perspective: 600px; }
-
-.reveal .slides > section, .reveal .slides > section > section {
- display: none;
- position: absolute;
- width: 100%;
- padding: 20px 0px;
- z-index: 10;
- -webkit-transform-style: preserve-3d;
- transform-style: preserve-3d;
- -webkit-transition: -webkit-transform-origin 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), -webkit-transform 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), visibility 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), opacity 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- transition: -ms-transform-origin 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), transform 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), visibility 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), opacity 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- transition: transform-origin 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), transform 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), visibility 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985), opacity 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
-
-/* Global transition speed settings */
-.reveal[data-transition-speed="fast"] .slides section {
- -webkit-transition-duration: 400ms;
- transition-duration: 400ms; }
-
-.reveal[data-transition-speed="slow"] .slides section {
- -webkit-transition-duration: 1200ms;
- transition-duration: 1200ms; }
-
-/* Slide-specific transition speed overrides */
-.reveal .slides section[data-transition-speed="fast"] {
- -webkit-transition-duration: 400ms;
- transition-duration: 400ms; }
-
-.reveal .slides section[data-transition-speed="slow"] {
- -webkit-transition-duration: 1200ms;
- transition-duration: 1200ms; }
-
-.reveal .slides > section.stack {
- padding-top: 0;
- padding-bottom: 0; }
-
-.reveal .slides > section.present, .reveal .slides > section > section.present {
- display: block;
- z-index: 11;
- opacity: 1; }
-
-.reveal.center, .reveal.center .slides, .reveal.center .slides section {
- min-height: 0 !important; }
-
-/* Don't allow interaction with invisible slides */
-.reveal .slides > section.future, .reveal .slides > section > section.future, .reveal .slides > section.past, .reveal .slides > section > section.past {
- pointer-events: none; }
-
-.reveal.overview .slides > section, .reveal.overview .slides > section > section {
- pointer-events: auto; }
-
-.reveal .slides > section.past, .reveal .slides > section.future, .reveal .slides > section > section.past, .reveal .slides > section > section.future {
- opacity: 0; }
-
-/*********************************************
- * SLIDE TRANSITION
- * Aliased 'linear' for backwards compatibility
- *********************************************/
-.reveal.slide section, .reveal.linear section {
- -webkit-backface-visibility: hidden;
- backface-visibility: hidden; }
-
-.reveal .slides > section[data-transition=slide].past, .reveal.slide .slides > section:not([data-transition]).past, .reveal .slides > section[data-transition=linear].past, .reveal.linear .slides > section:not([data-transition]).past {
- -webkit-transform: translate(-150%, 0);
- -ms-transform: translate(-150%, 0);
- transform: translate(-150%, 0); }
-
-.reveal .slides > section[data-transition=slide].future, .reveal.slide .slides > section:not([data-transition]).future, .reveal .slides > section[data-transition=linear].future, .reveal.linear .slides > section:not([data-transition]).future {
- -webkit-transform: translate(150%, 0);
- -ms-transform: translate(150%, 0);
- transform: translate(150%, 0); }
-
-.reveal .slides > section > section[data-transition=slide].past, .reveal.slide .slides > section > section:not([data-transition]).past, .reveal .slides > section > section[data-transition=linear].past, .reveal.linear .slides > section > section:not([data-transition]).past {
- -webkit-transform: translate(0, -150%);
- -ms-transform: translate(0, -150%);
- transform: translate(0, -150%); }
-
-.reveal .slides > section > section[data-transition=slide].future, .reveal.slide .slides > section > section:not([data-transition]).future, .reveal .slides > section > section[data-transition=linear].future, .reveal.linear .slides > section > section:not([data-transition]).future {
- -webkit-transform: translate(0, 150%);
- -ms-transform: translate(0, 150%);
- transform: translate(0, 150%); }
-
-/*********************************************
- * CONVEX TRANSITION
- * Aliased 'default' for backwards compatibility
- *********************************************/
-.reveal .slides > section[data-transition=default].past, .reveal.default .slides > section:not([data-transition]).past, .reveal .slides > section[data-transition=convex].past, .reveal.convex .slides > section:not([data-transition]).past {
- -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0);
- transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); }
-
-.reveal .slides > section[data-transition=default].future, .reveal.default .slides > section:not([data-transition]).future, .reveal .slides > section[data-transition=convex].future, .reveal.convex .slides > section:not([data-transition]).future {
- -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0);
- transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); }
-
-.reveal .slides > section > section[data-transition=default].past, .reveal.default .slides > section > section:not([data-transition]).past, .reveal .slides > section > section[data-transition=convex].past, .reveal.convex .slides > section > section:not([data-transition]).past {
- -webkit-transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0);
- transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0); }
-
-.reveal .slides > section > section[data-transition=default].future, .reveal.default .slides > section > section:not([data-transition]).future, .reveal .slides > section > section[data-transition=convex].future, .reveal.convex .slides > section > section:not([data-transition]).future {
- -webkit-transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0);
- transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0); }
-
-/*********************************************
- * CONCAVE TRANSITION
- *********************************************/
-.reveal .slides > section[data-transition=concave].past, .reveal.concave .slides > section:not([data-transition]).past {
- -webkit-transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0);
- transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); }
-
-.reveal .slides > section[data-transition=concave].future, .reveal.concave .slides > section:not([data-transition]).future {
- -webkit-transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0);
- transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); }
-
-.reveal .slides > section > section[data-transition=concave].past, .reveal.concave .slides > section > section:not([data-transition]).past {
- -webkit-transform: translate3d(0, -80%, 0) rotateX(-70deg) translate3d(0, -80%, 0);
- transform: translate3d(0, -80%, 0) rotateX(-70deg) translate3d(0, -80%, 0); }
-
-.reveal .slides > section > section[data-transition=concave].future, .reveal.concave .slides > section > section:not([data-transition]).future {
- -webkit-transform: translate3d(0, 80%, 0) rotateX(70deg) translate3d(0, 80%, 0);
- transform: translate3d(0, 80%, 0) rotateX(70deg) translate3d(0, 80%, 0); }
-
-/*********************************************
- * ZOOM TRANSITION
- *********************************************/
-.reveal .slides > section[data-transition=zoom], .reveal.zoom .slides > section:not([data-transition]) {
- -webkit-transition-timing-function: ease;
- transition-timing-function: ease; }
-
-.reveal .slides > section[data-transition=zoom].past, .reveal.zoom .slides > section:not([data-transition]).past {
- visibility: hidden;
- -webkit-transform: scale(16);
- -ms-transform: scale(16);
- transform: scale(16); }
-
-.reveal .slides > section[data-transition=zoom].future, .reveal.zoom .slides > section:not([data-transition]).future {
- visibility: hidden;
- -webkit-transform: scale(0.2);
- -ms-transform: scale(0.2);
- transform: scale(0.2); }
-
-.reveal .slides > section > section[data-transition=zoom].past, .reveal.zoom .slides > section > section:not([data-transition]).past {
- -webkit-transform: translate(0, -150%);
- -ms-transform: translate(0, -150%);
- transform: translate(0, -150%); }
-
-.reveal .slides > section > section[data-transition=zoom].future, .reveal.zoom .slides > section > section:not([data-transition]).future {
- -webkit-transform: translate(0, 150%);
- -ms-transform: translate(0, 150%);
- transform: translate(0, 150%); }
-
-/*********************************************
- * CUBE TRANSITION
- *********************************************/
-.reveal.cube .slides {
- -webkit-perspective: 1300px;
- perspective: 1300px; }
-
-.reveal.cube .slides section {
- padding: 30px;
- min-height: 700px;
- -webkit-backface-visibility: hidden;
- backface-visibility: hidden;
- -moz-box-sizing: border-box;
- box-sizing: border-box; }
-
-.reveal.center.cube .slides section {
- min-height: 0; }
-
-.reveal.cube .slides section:not(.stack):before {
- content: '';
- position: absolute;
- display: block;
- width: 100%;
- height: 100%;
- left: 0;
- top: 0;
- background: rgba(0, 0, 0, 0.1);
- border-radius: 4px;
- -webkit-transform: translateZ(-20px);
- transform: translateZ(-20px); }
-
-.reveal.cube .slides section:not(.stack):after {
- content: '';
- position: absolute;
- display: block;
- width: 90%;
- height: 30px;
- left: 5%;
- bottom: 0;
- background: none;
- z-index: 1;
- border-radius: 4px;
- box-shadow: 0px 95px 25px rgba(0, 0, 0, 0.2);
- -webkit-transform: translateZ(-90px) rotateX(65deg);
- transform: translateZ(-90px) rotateX(65deg); }
-
-.reveal.cube .slides > section.stack {
- padding: 0;
- background: none; }
-
-.reveal.cube .slides > section.past {
- -webkit-transform-origin: 100% 0%;
- -ms-transform-origin: 100% 0%;
- transform-origin: 100% 0%;
- -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg);
- transform: translate3d(-100%, 0, 0) rotateY(-90deg); }
-
-.reveal.cube .slides > section.future {
- -webkit-transform-origin: 0% 0%;
- -ms-transform-origin: 0% 0%;
- transform-origin: 0% 0%;
- -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg);
- transform: translate3d(100%, 0, 0) rotateY(90deg); }
-
-.reveal.cube .slides > section > section.past {
- -webkit-transform-origin: 0% 100%;
- -ms-transform-origin: 0% 100%;
- transform-origin: 0% 100%;
- -webkit-transform: translate3d(0, -100%, 0) rotateX(90deg);
- transform: translate3d(0, -100%, 0) rotateX(90deg); }
-
-.reveal.cube .slides > section > section.future {
- -webkit-transform-origin: 0% 0%;
- -ms-transform-origin: 0% 0%;
- transform-origin: 0% 0%;
- -webkit-transform: translate3d(0, 100%, 0) rotateX(-90deg);
- transform: translate3d(0, 100%, 0) rotateX(-90deg); }
-
-/*********************************************
- * PAGE TRANSITION
- *********************************************/
-.reveal.page .slides {
- -webkit-perspective-origin: 0% 50%;
- perspective-origin: 0% 50%;
- -webkit-perspective: 3000px;
- perspective: 3000px; }
-
-.reveal.page .slides section {
- padding: 30px;
- min-height: 700px;
- -moz-box-sizing: border-box;
- box-sizing: border-box; }
-
-.reveal.page .slides section.past {
- z-index: 12; }
-
-.reveal.page .slides section:not(.stack):before {
- content: '';
- position: absolute;
- display: block;
- width: 100%;
- height: 100%;
- left: 0;
- top: 0;
- background: rgba(0, 0, 0, 0.1);
- -webkit-transform: translateZ(-20px);
- transform: translateZ(-20px); }
-
-.reveal.page .slides section:not(.stack):after {
- content: '';
- position: absolute;
- display: block;
- width: 90%;
- height: 30px;
- left: 5%;
- bottom: 0;
- background: none;
- z-index: 1;
- border-radius: 4px;
- box-shadow: 0px 95px 25px rgba(0, 0, 0, 0.2);
- -webkit-transform: translateZ(-90px) rotateX(65deg); }
-
-.reveal.page .slides > section.stack {
- padding: 0;
- background: none; }
-
-.reveal.page .slides > section.past {
- -webkit-transform-origin: 0% 0%;
- -ms-transform-origin: 0% 0%;
- transform-origin: 0% 0%;
- -webkit-transform: translate3d(-40%, 0, 0) rotateY(-80deg);
- transform: translate3d(-40%, 0, 0) rotateY(-80deg); }
-
-.reveal.page .slides > section.future {
- -webkit-transform-origin: 100% 0%;
- -ms-transform-origin: 100% 0%;
- transform-origin: 100% 0%;
- -webkit-transform: translate3d(0, 0, 0);
- transform: translate3d(0, 0, 0); }
-
-.reveal.page .slides > section > section.past {
- -webkit-transform-origin: 0% 0%;
- -ms-transform-origin: 0% 0%;
- transform-origin: 0% 0%;
- -webkit-transform: translate3d(0, -40%, 0) rotateX(80deg);
- transform: translate3d(0, -40%, 0) rotateX(80deg); }
-
-.reveal.page .slides > section > section.future {
- -webkit-transform-origin: 0% 100%;
- -ms-transform-origin: 0% 100%;
- transform-origin: 0% 100%;
- -webkit-transform: translate3d(0, 0, 0);
- transform: translate3d(0, 0, 0); }
-
-/*********************************************
- * FADE TRANSITION
- *********************************************/
-.reveal .slides section[data-transition=fade], .reveal.fade .slides section:not([data-transition]), .reveal.fade .slides > section > section:not([data-transition]) {
- -webkit-transform: none;
- -ms-transform: none;
- transform: none;
- -webkit-transition: opacity 0.5s;
- transition: opacity 0.5s; }
-
-.reveal.fade.overview .slides section, .reveal.fade.overview .slides > section > section {
- -webkit-transition: none;
- transition: none; }
-
-/*********************************************
- * NO TRANSITION
- *********************************************/
-.reveal .slides section[data-transition=none], .reveal.none .slides section:not([data-transition]) {
- -webkit-transform: none;
- -ms-transform: none;
- transform: none;
- -webkit-transition: none;
- transition: none; }
-
-/*********************************************
- * OVERVIEW
- *********************************************/
-.reveal.overview .slides {
- -webkit-perspective-origin: 50% 50%;
- perspective-origin: 50% 50%;
- -webkit-perspective: 700px;
- perspective: 700px; }
-
-.reveal.overview .slides section {
- height: 700px;
- overflow: hidden;
- opacity: 1 !important;
- visibility: visible !important;
- cursor: pointer;
- background: rgba(0, 0, 0, 0.1);
- -moz-box-sizing: border-box;
- box-sizing: border-box; }
-
-.reveal.overview .slides section, .reveal.overview-deactivating .slides section {
- -webkit-transition: none !important;
- transition: none !important; }
-
-.reveal.overview .slides section .fragment {
- opacity: 1; }
-
-.reveal.overview .slides section:after, .reveal.overview .slides section:before {
- display: none !important; }
-
-.reveal.overview .slides section > section {
- opacity: 1;
- cursor: pointer; }
-
-.reveal.overview .slides section:hover {
- background: rgba(0, 0, 0, 0.3); }
-
-.reveal.overview .slides section.present {
- background: rgba(0, 0, 0, 0.3); }
-
-.reveal.overview .slides > section.stack {
- padding: 0;
- top: 0 !important;
- background: none;
- overflow: visible; }
-
-/*********************************************
- * PAUSED MODE
- *********************************************/
-.reveal .pause-overlay {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: black;
- visibility: hidden;
- opacity: 0;
- z-index: 100;
- -webkit-transition: all 1s ease;
- transition: all 1s ease; }
-
-.reveal.paused .pause-overlay {
- visibility: visible;
- opacity: 1; }
-
-/*********************************************
- * FALLBACK
- *********************************************/
-.no-transforms {
- overflow-y: auto; }
-
-.no-transforms .reveal .slides {
- position: relative;
- width: 80%;
- height: auto !important;
- top: 0;
- left: 50%;
- margin: 0;
- text-align: center; }
-
-.no-transforms .reveal .controls, .no-transforms .reveal .progress {
- display: none !important; }
-
-.no-transforms .reveal .slides section {
- display: block !important;
- opacity: 1 !important;
- position: relative !important;
- height: auto;
- min-height: 0;
- top: 0;
- left: -50%;
- margin: 70px 0;
- -webkit-transform: none;
- -ms-transform: none;
- transform: none; }
-
-.no-transforms .reveal .slides section section {
- left: 0; }
-
-.reveal .no-transition, .reveal .no-transition * {
- -webkit-transition: none !important;
- transition: none !important; }
-
-/*********************************************
- * PER-SLIDE BACKGROUNDS
- *********************************************/
-.reveal > .backgrounds {
- position: absolute;
- width: 100%;
- height: 100%;
- top: 0;
- left: 0;
- -webkit-perspective: 600px;
- perspective: 600px; }
-
-.reveal .slide-background {
- display: none;
- position: absolute;
- width: 100%;
- height: 100%;
- opacity: 0;
- visibility: hidden;
- background-color: rgba(0, 0, 0, 0);
- background-position: 50% 50%;
- background-repeat: no-repeat;
- background-size: cover;
- -webkit-transition: all 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- transition: all 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
-
-.reveal .slide-background.stack {
- display: block; }
-
-.reveal .slide-background.present {
- opacity: 1;
- visibility: visible; }
-
-.print-pdf .reveal .slide-background {
- opacity: 1 !important;
- visibility: visible !important; }
-
-/* Video backgrounds */
-.reveal .slide-background video {
- position: absolute;
- width: 100%;
- height: 100%;
- max-width: none;
- max-height: none;
- top: 0;
- left: 0; }
-
-/* Immediate transition style */
-.reveal[data-background-transition=none] > .backgrounds .slide-background, .reveal > .backgrounds .slide-background[data-background-transition=none] {
- -webkit-transition: none;
- transition: none; }
-
-/* Slide */
-.reveal[data-background-transition=slide] > .backgrounds .slide-background, .reveal > .backgrounds .slide-background[data-background-transition=slide] {
- opacity: 1;
- -webkit-backface-visibility: hidden;
- backface-visibility: hidden; }
-
-.reveal[data-background-transition=slide] > .backgrounds .slide-background.past, .reveal > .backgrounds .slide-background.past[data-background-transition=slide] {
- -webkit-transform: translate(-100%, 0);
- -ms-transform: translate(-100%, 0);
- transform: translate(-100%, 0); }
-
-.reveal[data-background-transition=slide] > .backgrounds .slide-background.future, .reveal > .backgrounds .slide-background.future[data-background-transition=slide] {
- -webkit-transform: translate(100%, 0);
- -ms-transform: translate(100%, 0);
- transform: translate(100%, 0); }
-
-.reveal[data-background-transition=slide] > .backgrounds .slide-background > .slide-background.past, .reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=slide] {
- -webkit-transform: translate(0, -100%);
- -ms-transform: translate(0, -100%);
- transform: translate(0, -100%); }
-
-.reveal[data-background-transition=slide] > .backgrounds .slide-background > .slide-background.future, .reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=slide] {
- -webkit-transform: translate(0, 100%);
- -ms-transform: translate(0, 100%);
- transform: translate(0, 100%); }
-
-/* Convex */
-.reveal[data-background-transition=convex] > .backgrounds .slide-background.past, .reveal > .backgrounds .slide-background.past[data-background-transition=convex] {
- opacity: 0;
- -webkit-transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0);
- transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0); }
-
-.reveal[data-background-transition=convex] > .backgrounds .slide-background.future, .reveal > .backgrounds .slide-background.future[data-background-transition=convex] {
- opacity: 0;
- -webkit-transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0);
- transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0); }
-
-.reveal[data-background-transition=convex] > .backgrounds .slide-background > .slide-background.past, .reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=convex] {
- opacity: 0;
- -webkit-transform: translate3d(0, -100%, 0) rotateX(90deg) translate3d(0, -100%, 0);
- transform: translate3d(0, -100%, 0) rotateX(90deg) translate3d(0, -100%, 0); }
-
-.reveal[data-background-transition=convex] > .backgrounds .slide-background > .slide-background.future, .reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=convex] {
- opacity: 0;
- -webkit-transform: translate3d(0, 100%, 0) rotateX(-90deg) translate3d(0, 100%, 0);
- transform: translate3d(0, 100%, 0) rotateX(-90deg) translate3d(0, 100%, 0); }
-
-/* Concave */
-.reveal[data-background-transition=concave] > .backgrounds .slide-background.past, .reveal > .backgrounds .slide-background.past[data-background-transition=concave] {
- opacity: 0;
- -webkit-transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0);
- transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0); }
-
-.reveal[data-background-transition=concave] > .backgrounds .slide-background.future, .reveal > .backgrounds .slide-background.future[data-background-transition=concave] {
- opacity: 0;
- -webkit-transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0);
- transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0); }
-
-.reveal[data-background-transition=concave] > .backgrounds .slide-background > .slide-background.past, .reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=concave] {
- opacity: 0;
- -webkit-transform: translate3d(0, -100%, 0) rotateX(-90deg) translate3d(0, -100%, 0);
- transform: translate3d(0, -100%, 0) rotateX(-90deg) translate3d(0, -100%, 0); }
-
-.reveal[data-background-transition=concave] > .backgrounds .slide-background > .slide-background.future, .reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=concave] {
- opacity: 0;
- -webkit-transform: translate3d(0, 100%, 0) rotateX(90deg) translate3d(0, 100%, 0);
- transform: translate3d(0, 100%, 0) rotateX(90deg) translate3d(0, 100%, 0); }
-
-/* Zoom */
-.reveal[data-background-transition=zoom] > .backgrounds .slide-background, .reveal > .backgrounds .slide-background[data-background-transition=zoom] {
- -webkit-transition-timing-function: ease;
- transition-timing-function: ease; }
-
-.reveal[data-background-transition=zoom] > .backgrounds .slide-background.past, .reveal > .backgrounds .slide-background.past[data-background-transition=zoom] {
- opacity: 0;
- visibility: hidden;
- -webkit-transform: scale(16);
- -ms-transform: scale(16);
- transform: scale(16); }
-
-.reveal[data-background-transition=zoom] > .backgrounds .slide-background.future, .reveal > .backgrounds .slide-background.future[data-background-transition=zoom] {
- opacity: 0;
- visibility: hidden;
- -webkit-transform: scale(0.2);
- -ms-transform: scale(0.2);
- transform: scale(0.2); }
-
-.reveal[data-background-transition=zoom] > .backgrounds .slide-background > .slide-background.past, .reveal > .backgrounds .slide-background > .slide-background.past[data-background-transition=zoom] {
- opacity: 0;
- visibility: hidden;
- -webkit-transform: scale(16);
- -ms-transform: scale(16);
- transform: scale(16); }
-
-.reveal[data-background-transition=zoom] > .backgrounds .slide-background > .slide-background.future, .reveal > .backgrounds .slide-background > .slide-background.future[data-background-transition=zoom] {
- opacity: 0;
- visibility: hidden;
- -webkit-transform: scale(0.2);
- -ms-transform: scale(0.2);
- transform: scale(0.2); }
-
-/* Global transition speed settings */
-.reveal[data-transition-speed="fast"] > .backgrounds .slide-background {
- -webkit-transition-duration: 400ms;
- transition-duration: 400ms; }
-
-.reveal[data-transition-speed="slow"] > .backgrounds .slide-background {
- -webkit-transition-duration: 1200ms;
- transition-duration: 1200ms; }
-
-/*********************************************
- * RTL SUPPORT
- *********************************************/
-.reveal.rtl .slides, .reveal.rtl .slides h1, .reveal.rtl .slides h2, .reveal.rtl .slides h3, .reveal.rtl .slides h4, .reveal.rtl .slides h5, .reveal.rtl .slides h6 {
- direction: rtl;
- font-family: sans-serif; }
-
-.reveal.rtl pre, .reveal.rtl code {
- direction: ltr; }
-
-.reveal.rtl ol, .reveal.rtl ul {
- text-align: right; }
-
-.reveal.rtl .progress span {
- float: right; }
-
-/*********************************************
- * PARALLAX BACKGROUND
- *********************************************/
-.reveal.has-parallax-background .backgrounds {
- -webkit-transition: all 0.8s ease;
- transition: all 0.8s ease; }
-
-/* Global transition speed settings */
-.reveal.has-parallax-background[data-transition-speed="fast"] .backgrounds {
- -webkit-transition-duration: 400ms;
- transition-duration: 400ms; }
-
-.reveal.has-parallax-background[data-transition-speed="slow"] .backgrounds {
- -webkit-transition-duration: 1200ms;
- transition-duration: 1200ms; }
-
-/*********************************************
- * LINK PREVIEW OVERLAY
- *********************************************/
-.reveal .overlay {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 1000;
- background: rgba(0, 0, 0, 0.9);
- opacity: 0;
- visibility: hidden;
- -webkit-transition: all 0.3s ease;
- transition: all 0.3s ease; }
-
-.reveal .overlay.visible {
- opacity: 1;
- visibility: visible; }
-
-.reveal .overlay .spinner {
- position: absolute;
- display: block;
- top: 50%;
- left: 50%;
- width: 32px;
- height: 32px;
- margin: -16px 0 0 -16px;
- z-index: 10;
- background-image: url(data:image/gif;base64,R0lGODlhIAAgAPMAAJmZmf%2F%2F%2F6%2Bvr8nJybW1tcDAwOjo6Nvb26ioqKOjo7Ozs%2FLy8vz8%2FAAAAAAAAAAAACH%2FC05FVFNDQVBFMi4wAwEAAAAh%2FhpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh%2BQQJCgAAACwAAAAAIAAgAAAE5xDISWlhperN52JLhSSdRgwVo1ICQZRUsiwHpTJT4iowNS8vyW2icCF6k8HMMBkCEDskxTBDAZwuAkkqIfxIQyhBQBFvAQSDITM5VDW6XNE4KagNh6Bgwe60smQUB3d4Rz1ZBApnFASDd0hihh12BkE9kjAJVlycXIg7CQIFA6SlnJ87paqbSKiKoqusnbMdmDC2tXQlkUhziYtyWTxIfy6BE8WJt5YJvpJivxNaGmLHT0VnOgSYf0dZXS7APdpB309RnHOG5gDqXGLDaC457D1zZ%2FV%2FnmOM82XiHRLYKhKP1oZmADdEAAAh%2BQQJCgAAACwAAAAAIAAgAAAE6hDISWlZpOrNp1lGNRSdRpDUolIGw5RUYhhHukqFu8DsrEyqnWThGvAmhVlteBvojpTDDBUEIFwMFBRAmBkSgOrBFZogCASwBDEY%2FCZSg7GSE0gSCjQBMVG023xWBhklAnoEdhQEfyNqMIcKjhRsjEdnezB%2BA4k8gTwJhFuiW4dokXiloUepBAp5qaKpp6%2BHo7aWW54wl7obvEe0kRuoplCGepwSx2jJvqHEmGt6whJpGpfJCHmOoNHKaHx61WiSR92E4lbFoq%2BB6QDtuetcaBPnW6%2BO7wDHpIiK9SaVK5GgV543tzjgGcghAgAh%2BQQJCgAAACwAAAAAIAAgAAAE7hDISSkxpOrN5zFHNWRdhSiVoVLHspRUMoyUakyEe8PTPCATW9A14E0UvuAKMNAZKYUZCiBMuBakSQKG8G2FzUWox2AUtAQFcBKlVQoLgQReZhQlCIJesQXI5B0CBnUMOxMCenoCfTCEWBsJColTMANldx15BGs8B5wlCZ9Po6OJkwmRpnqkqnuSrayqfKmqpLajoiW5HJq7FL1Gr2mMMcKUMIiJgIemy7xZtJsTmsM4xHiKv5KMCXqfyUCJEonXPN2rAOIAmsfB3uPoAK%2B%2BG%2Bw48edZPK%2BM6hLJpQg484enXIdQFSS1u6UhksENEQAAIfkECQoAAAAsAAAAACAAIAAABOcQyEmpGKLqzWcZRVUQnZYg1aBSh2GUVEIQ2aQOE%2BG%2BcD4ntpWkZQj1JIiZIogDFFyHI0UxQwFugMSOFIPJftfVAEoZLBbcLEFhlQiqGp1Vd140AUklUN3eCA51C1EWMzMCezCBBmkxVIVHBWd3HHl9JQOIJSdSnJ0TDKChCwUJjoWMPaGqDKannasMo6WnM562R5YluZRwur0wpgqZE7NKUm%2BFNRPIhjBJxKZteWuIBMN4zRMIVIhffcgojwCF117i4nlLnY5ztRLsnOk%2BaV%2BoJY7V7m76PdkS4trKcdg0Zc0tTcKkRAAAIfkECQoAAAAsAAAAACAAIAAABO4QyEkpKqjqzScpRaVkXZWQEximw1BSCUEIlDohrft6cpKCk5xid5MNJTaAIkekKGQkWyKHkvhKsR7ARmitkAYDYRIbUQRQjWBwJRzChi9CRlBcY1UN4g0%2FVNB0AlcvcAYHRyZPdEQFYV8ccwR5HWxEJ02YmRMLnJ1xCYp0Y5idpQuhopmmC2KgojKasUQDk5BNAwwMOh2RtRq5uQuPZKGIJQIGwAwGf6I0JXMpC8C7kXWDBINFMxS4DKMAWVWAGYsAdNqW5uaRxkSKJOZKaU3tPOBZ4DuK2LATgJhkPJMgTwKCdFjyPHEnKxFCDhEAACH5BAkKAAAALAAAAAAgACAAAATzEMhJaVKp6s2nIkolIJ2WkBShpkVRWqqQrhLSEu9MZJKK9y1ZrqYK9WiClmvoUaF8gIQSNeF1Er4MNFn4SRSDARWroAIETg1iVwuHjYB1kYc1mwruwXKC9gmsJXliGxc%2BXiUCby9ydh1sOSdMkpMTBpaXBzsfhoc5l58Gm5yToAaZhaOUqjkDgCWNHAULCwOLaTmzswadEqggQwgHuQsHIoZCHQMMQgQGubVEcxOPFAcMDAYUA85eWARmfSRQCdcMe0zeP1AAygwLlJtPNAAL19DARdPzBOWSm1brJBi45soRAWQAAkrQIykShQ9wVhHCwCQCACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiRMDjI0Fd30%2FiI2UA5GSS5UDj2l6NoqgOgN4gksEBgYFf0FDqKgHnyZ9OX8HrgYHdHpcHQULXAS2qKpENRg7eAMLC7kTBaixUYFkKAzWAAnLC7FLVxLWDBLKCwaKTULgEwbLA4hJtOkSBNqITT3xEgfLpBtzE%2FjiuL04RGEBgwWhShRgQExHBAAh%2BQQJCgAAACwAAAAAIAAgAAAE7xDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfZiCqGk5dTESJeaOAlClzsJsqwiJwiqnFrb2nS9kmIcgEsjQydLiIlHehhpejaIjzh9eomSjZR%2BipslWIRLAgMDOR2DOqKogTB9pCUJBagDBXR6XB0EBkIIsaRsGGMMAxoDBgYHTKJiUYEGDAzHC9EACcUGkIgFzgwZ0QsSBcXHiQvOwgDdEwfFs0sDzt4S6BK4xYjkDOzn0unFeBzOBijIm1Dgmg5YFQwsCMjp1oJ8LyIAACH5BAkKAAAALAAAAAAgACAAAATwEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GGl6NoiPOH16iZKNlH6KmyWFOggHhEEvAwwMA0N9GBsEC6amhnVcEwavDAazGwIDaH1ipaYLBUTCGgQDA8NdHz0FpqgTBwsLqAbWAAnIA4FWKdMLGdYGEgraigbT0OITBcg5QwPT4xLrROZL6AuQAPUS7bxLpoWidY0JtxLHKhwwMJBTHgPKdEQAACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GAULDJCRiXo1CpGXDJOUjY%2BYip9DhToJA4RBLwMLCwVDfRgbBAaqqoZ1XBMHswsHtxtFaH1iqaoGNgAIxRpbFAgfPQSqpbgGBqUD1wBXeCYp1AYZ19JJOYgH1KwA4UBvQwXUBxPqVD9L3sbp2BNk2xvvFPJd%2BMFCN6HAAIKgNggY0KtEBAAh%2BQQJCgAAACwAAAAAIAAgAAAE6BDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfYIDMaAFdTESJeaEDAIMxYFqrOUaNW4E4ObYcCXaiBVEgULe0NJaxxtYksjh2NLkZISgDgJhHthkpU4mW6blRiYmZOlh4JWkDqILwUGBnE6TYEbCgevr0N1gH4At7gHiRpFaLNrrq8HNgAJA70AWxQIH1%2BvsYMDAzZQPC9VCNkDWUhGkuE5PxJNwiUK4UfLzOlD4WvzAHaoG9nxPi5d%2BjYUqfAhhykOFwJWiAAAIfkECQoAAAAsAAAAACAAIAAABPAQyElpUqnqzaciSoVkXVUMFaFSwlpOCcMYlErAavhOMnNLNo8KsZsMZItJEIDIFSkLGQoQTNhIsFehRww2CQLKF0tYGKYSg%2BygsZIuNqJksKgbfgIGepNo2cIUB3V1B3IvNiBYNQaDSTtfhhx0CwVPI0UJe0%2Bbm4g5VgcGoqOcnjmjqDSdnhgEoamcsZuXO1aWQy8KAwOAuTYYGwi7w5h%2BKr0SJ8MFihpNbx%2B4Erq7BYBuzsdiH1jCAzoSfl0rVirNbRXlBBlLX%2BBP0XJLAPGzTkAuAOqb0WT5AH7OcdCm5B8TgRwSRKIHQtaLCwg1RAAAOwAAAAAAAAAAAA%3D%3D);
- visibility: visible;
- opacity: 0.6;
- -webkit-transition: all 0.3s ease;
- transition: all 0.3s ease; }
-
-.reveal .overlay header {
- position: absolute;
- left: 0;
- top: 0;
- width: 100%;
- height: 40px;
- z-index: 2;
- border-bottom: 1px solid #222; }
-
-.reveal .overlay header a {
- display: inline-block;
- width: 40px;
- height: 40px;
- padding: 0 10px;
- float: right;
- opacity: 0.6;
- -moz-box-sizing: border-box;
- box-sizing: border-box; }
-
-.reveal .overlay header a:hover {
- opacity: 1; }
-
-.reveal .overlay header a .icon {
- display: inline-block;
- width: 20px;
- height: 20px;
- background-position: 50% 50%;
- background-size: 100%;
- background-repeat: no-repeat; }
-
-.reveal .overlay header a.close .icon {
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABkklEQVRYR8WX4VHDMAxG6wnoJrABZQPYBCaBTWAD2g1gE5gg6OOsXuxIlr40d81dfrSJ9V4c2VLK7spHuTJ/5wpM07QXuXc5X0opX2tEJcadjHuV80li/FgxTIEK/5QBCICBD6xEhSMGHgQPgBgLiYVAB1dpSqKDawxTohFw4JSEA3clzgIBPCURwE2JucBR7rhPJJv5OpJwDX+SfDjgx1wACQeJG1aChP9K/IMmdZ8DtESV1WyP3Bt4MwM6sj4NMxMYiqUWHQu4KYA/SYkIjOsm3BXYWMKFDwU2khjCQ4ELJUJ4SmClRArOCmSXGuKma0fYD5CbzHxFpCSGAhfAVSSUGDUk2BWZaff2g6GE15BsBQ9nwmpIGDiyHQddwNTMKkbZaf9fajXQca1EX44puJZUsnY0ObGmITE3GVLCbEhQUjGVt146j6oasWN+49Vph2w1pZ5EansNZqKBm1txbU57iRRcZ86RWMDdWtBJUHBHwoQPi1GV+JCbntmvok7iTX4/Up9mgyTc/FJYDTcndgH/AA5A/CHsyEkVAAAAAElFTkSuQmCC); }
-
-.reveal .overlay header a.external .icon {
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAcElEQVRYR+2WSQoAIQwEzf8f7XiOMkUQxUPlGkM3hVmiQfQR9GYnH1SsAQlI4DiBqkCMoNb9y2e90IAEJPAcgdznU9+engMaeJ7Azh5Y1U67gAho4DqBqmB1buAf0MB1AlVBek83ZPkmJMGc1wAR+AAqod/B97TRpQAAAABJRU5ErkJggg==); }
-
-.reveal .overlay .viewport {
- position: absolute;
- top: 40px;
- right: 0;
- bottom: 0;
- left: 0; }
-
-.reveal .overlay.overlay-preview .viewport iframe {
- width: 100%;
- height: 100%;
- max-width: 100%;
- max-height: 100%;
- border: 0;
- opacity: 0;
- visibility: hidden;
- -webkit-transition: all 0.3s ease;
- transition: all 0.3s ease; }
-
-.reveal .overlay.overlay-preview.loaded .viewport iframe {
- opacity: 1;
- visibility: visible; }
-
-.reveal .overlay.overlay-preview.loaded .spinner {
- opacity: 0;
- visibility: hidden;
- -webkit-transform: scale(0.2);
- -ms-transform: scale(0.2);
- transform: scale(0.2); }
-
-.reveal .overlay.overlay-help .viewport {
- overflow: auto;
- color: #fff; }
-
-.reveal .overlay.overlay-help .viewport .viewport-inner {
- width: 600px;
- margin: 0 auto;
- padding: 60px;
- text-align: center;
- letter-spacing: normal; }
-
-.reveal .overlay.overlay-help .viewport .viewport-inner .title {
- font-size: 20px; }
-
-.reveal .overlay.overlay-help .viewport .viewport-inner table {
- border: 1px solid #fff;
- border-collapse: collapse;
- font-size: 14px; }
-
-.reveal .overlay.overlay-help .viewport .viewport-inner table th, .reveal .overlay.overlay-help .viewport .viewport-inner table td {
- width: 200px;
- padding: 10px;
- border: 1px solid #fff;
- vertical-align: middle; }
-
-.reveal .overlay.overlay-help .viewport .viewport-inner table th {
- padding-top: 20px;
- padding-bottom: 20px; }
-
-/*********************************************
- * PLAYBACK COMPONENT
- *********************************************/
-.reveal .playback {
- position: fixed;
- left: 15px;
- bottom: 15px;
- z-index: 30;
- cursor: pointer;
- -webkit-transition: all 400ms ease;
- transition: all 400ms ease; }
-
-.reveal.overview .playback {
- opacity: 0;
- visibility: hidden; }
-
-/*********************************************
- * ROLLING LINKS
- *********************************************/
-.reveal .roll {
- display: inline-block;
- line-height: 1.2;
- overflow: hidden;
- vertical-align: top;
- -webkit-perspective: 400px;
- perspective: 400px;
- -webkit-perspective-origin: 50% 50%;
- perspective-origin: 50% 50%; }
-
-.reveal .roll:hover {
- background: none;
- text-shadow: none; }
-
-.reveal .roll span {
- display: block;
- position: relative;
- padding: 0 2px;
- pointer-events: none;
- -webkit-transition: all 400ms ease;
- transition: all 400ms ease;
- -webkit-transform-origin: 50% 0%;
- -ms-transform-origin: 50% 0%;
- transform-origin: 50% 0%;
- -webkit-transform-style: preserve-3d;
- transform-style: preserve-3d;
- -webkit-backface-visibility: hidden;
- backface-visibility: hidden; }
-
-.reveal .roll:hover span {
- background: rgba(0, 0, 0, 0.5);
- -webkit-transform: translate3d(0px, 0px, -45px) rotateX(90deg);
- transform: translate3d(0px, 0px, -45px) rotateX(90deg); }
-
-.reveal .roll span:after {
- content: attr(data-title);
- display: block;
- position: absolute;
- left: 0;
- top: 0;
- padding: 0 2px;
- -webkit-backface-visibility: hidden;
- backface-visibility: hidden;
- -webkit-transform-origin: 50% 0%;
- -ms-transform-origin: 50% 0%;
- transform-origin: 50% 0%;
- -webkit-transform: translate3d(0px, 110%, 0px) rotateX(-90deg);
- transform: translate3d(0px, 110%, 0px) rotateX(-90deg); }
-
-/*********************************************
- * SPEAKER NOTES
- *********************************************/
-.reveal aside.notes {
- display: none; }
-
-/*********************************************
- * ZOOM PLUGIN
- *********************************************/
-.zoomed .reveal *, .zoomed .reveal *:before, .zoomed .reveal *:after {
- -webkit-backface-visibility: visible !important;
- backface-visibility: visible !important; }
-
-.zoomed .reveal .progress, .zoomed .reveal .controls {
- opacity: 0; }
-
-.zoomed .reveal .roll span {
- background: none; }
-
-.zoomed .reveal .roll span:after {
- visibility: hidden; }
diff --git a/presentation/css/reveal.scss b/presentation/css/reveal.scss
deleted file mode 100644
index 6cc21baaa..000000000
--- a/presentation/css/reveal.scss
+++ /dev/null
@@ -1,1316 +0,0 @@
-/*!
- * reveal.js
- * http://lab.hakim.se/reveal-js
- * MIT licensed
- *
- * Copyright (C) 2015 Hakim El Hattab, http://hakim.se
- */
-
-
-/*********************************************
- * RESET STYLES
- *********************************************/
-
-html, body, .reveal div, .reveal span, .reveal applet, .reveal object, .reveal iframe,
-.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6, .reveal p, .reveal blockquote, .reveal pre,
-.reveal a, .reveal abbr, .reveal acronym, .reveal address, .reveal big, .reveal cite, .reveal code,
-.reveal del, .reveal dfn, .reveal em, .reveal img, .reveal ins, .reveal kbd, .reveal q, .reveal s, .reveal samp,
-.reveal small, .reveal strike, .reveal strong, .reveal sub, .reveal sup, .reveal tt, .reveal var,
-.reveal b, .reveal u, .reveal center,
-.reveal dl, .reveal dt, .reveal dd, .reveal ol, .reveal ul, .reveal li,
-.reveal fieldset, .reveal form, .reveal label, .reveal legend,
-.reveal table, .reveal caption, .reveal tbody, .reveal tfoot, .reveal thead, .reveal tr, .reveal th, .reveal td,
-.reveal article, .reveal aside, .reveal canvas, .reveal details, .reveal embed,
-.reveal figure, .reveal figcaption, .reveal footer, .reveal header, .reveal hgroup,
-.reveal menu, .reveal nav, .reveal output, .reveal ruby, .reveal section, .reveal summary,
-.reveal time, .reveal mark, .reveal audio, video {
- margin: 0;
- padding: 0;
- border: 0;
- font-size: 100%;
- font: inherit;
- vertical-align: baseline;
-}
-
-.reveal article, .reveal aside, .reveal details, .reveal figcaption, .reveal figure,
-.reveal footer, .reveal header, .reveal hgroup, .reveal menu, .reveal nav, .reveal section {
- display: block;
-}
-
-
-/*********************************************
- * GLOBAL STYLES
- *********************************************/
-
-html,
-body {
- width: 100%;
- height: 100%;
- overflow: hidden;
-}
-
-body {
- position: relative;
- line-height: 1;
-
- background-color: #fff;
- color: #000;
-}
-
-::-moz-selection {
- background: #FF5E99;
- color: #fff;
- text-shadow: none;
-}
-
-::selection {
- background: #FF5E99;
- color: #fff;
- text-shadow: none;
-}
-
-
-/*********************************************
- * VIEW FRAGMENTS
- *********************************************/
-
-.reveal .slides section .fragment {
- opacity: 0;
- visibility: hidden;
- transition: all .2s ease;
-
- &.visible {
- opacity: 1;
- visibility: visible;
- }
-}
-
-.reveal .slides section .fragment.grow {
- opacity: 1;
- visibility: visible;
-
- &.visible {
- transform: scale( 1.3 );
- }
-}
-
-.reveal .slides section .fragment.shrink {
- opacity: 1;
- visibility: visible;
-
- &.visible {
- transform: scale( 0.7 );
- }
-}
-
-.reveal .slides section .fragment.zoom-in {
- transform: scale( 0.1 );
-
- &.visible {
- transform: scale( 1 );
- }
-}
-
-.reveal .slides section .fragment.roll-in {
- transform: rotateX( 90deg );
-
- &.visible {
- transform: rotateX( 0 );
- }
-}
-
-.reveal .slides section .fragment.fade-out {
- opacity: 1;
- visibility: visible;
-
- &.visible {
- opacity: 0;
- visibility: hidden;
- }
-}
-
-.reveal .slides section .fragment.semi-fade-out {
- opacity: 1;
- visibility: visible;
-
- &.visible {
- opacity: 0.5;
- visibility: visible;
- }
-}
-
-.reveal .slides section .fragment.strike {
- opacity: 1;
-
- &.visible {
- text-decoration: line-through;
- }
-}
-
-.reveal .slides section .fragment.current-visible {
- opacity: 0;
- visibility: hidden;
-
- &.current-fragment {
- opacity: 1;
- visibility: visible;
- }
-}
-
-.reveal .slides section .fragment.highlight-red,
-.reveal .slides section .fragment.highlight-current-red,
-.reveal .slides section .fragment.highlight-green,
-.reveal .slides section .fragment.highlight-current-green,
-.reveal .slides section .fragment.highlight-blue,
-.reveal .slides section .fragment.highlight-current-blue {
- opacity: 1;
- visibility: visible;
-}
- .reveal .slides section .fragment.highlight-red.visible {
- color: #ff2c2d
- }
- .reveal .slides section .fragment.highlight-green.visible {
- color: #17ff2e;
- }
- .reveal .slides section .fragment.highlight-blue.visible {
- color: #1b91ff;
- }
-
-.reveal .slides section .fragment.highlight-current-red.current-fragment {
- color: #ff2c2d
-}
-.reveal .slides section .fragment.highlight-current-green.current-fragment {
- color: #17ff2e;
-}
-.reveal .slides section .fragment.highlight-current-blue.current-fragment {
- color: #1b91ff;
-}
-
-
-/*********************************************
- * DEFAULT ELEMENT STYLES
- *********************************************/
-
-/* Fixes issue in Chrome where italic fonts did not appear when printing to PDF */
-.reveal:after {
- content: '';
- font-style: italic;
-}
-
-.reveal iframe {
- z-index: 1;
-}
-
-/** Prevents layering issues in certain browser/transition combinations */
-.reveal a {
- position: relative;
-}
-
-.reveal .stretch {
- max-width: none;
- max-height: none;
-}
-
-.reveal pre.stretch code {
- height: 100%;
- max-height: 100%;
- box-sizing: border-box;
-}
-
-
-/*********************************************
- * CONTROLS
- *********************************************/
-
-.reveal .controls {
- display: none;
- position: fixed;
- width: 110px;
- height: 110px;
- z-index: 30;
- right: 10px;
- bottom: 10px;
-
- -webkit-user-select: none;
-}
-
-.reveal .controls div {
- position: absolute;
- opacity: 0.05;
- width: 0;
- height: 0;
- border: 12px solid transparent;
- transform: scale(.9999);
- transition: all 0.2s ease;
-
- -webkit-tap-highlight-color: rgba( 0, 0, 0, 0 );
-}
-
-.reveal .controls div.enabled {
- opacity: 0.7;
- cursor: pointer;
-}
-
-.reveal .controls div.enabled:active {
- margin-top: 1px;
-}
-
- .reveal .controls div.navigate-left {
- top: 42px;
-
- border-right-width: 22px;
- border-right-color: #000;
- }
- .reveal .controls div.navigate-left.fragmented {
- opacity: 0.3;
- }
-
- .reveal .controls div.navigate-right {
- left: 74px;
- top: 42px;
-
- border-left-width: 22px;
- border-left-color: #000;
- }
- .reveal .controls div.navigate-right.fragmented {
- opacity: 0.3;
- }
-
- .reveal .controls div.navigate-up {
- left: 42px;
-
- border-bottom-width: 22px;
- border-bottom-color: #000;
- }
- .reveal .controls div.navigate-up.fragmented {
- opacity: 0.3;
- }
-
- .reveal .controls div.navigate-down {
- left: 42px;
- top: 74px;
-
- border-top-width: 22px;
- border-top-color: #000;
- }
- .reveal .controls div.navigate-down.fragmented {
- opacity: 0.3;
- }
-
-
-/*********************************************
- * PROGRESS BAR
- *********************************************/
-
-.reveal .progress {
- position: fixed;
- display: none;
- height: 3px;
- width: 100%;
- bottom: 0;
- left: 0;
- z-index: 10;
-
- background-color: rgba( 0, 0, 0, 0.2 );
-}
- .reveal .progress:after {
- content: '';
- display: block;
- position: absolute;
- height: 20px;
- width: 100%;
- top: -20px;
- }
- .reveal .progress span {
- display: block;
- height: 100%;
- width: 0px;
-
- background-color: #000;
- transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985);
- }
-
-/*********************************************
- * SLIDE NUMBER
- *********************************************/
-
-.reveal .slide-number {
- position: fixed;
- display: block;
- right: 15px;
- bottom: 15px;
- opacity: 0.5;
- z-index: 31;
- font-size: 12px;
-}
-
-/*********************************************
- * SLIDES
- *********************************************/
-
-.reveal {
- position: relative;
- width: 100%;
- height: 100%;
- touch-action: none;
-}
-
-.reveal .slides {
- position: absolute;
- width: 100%;
- height: 100%;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- margin: auto;
-
- overflow: visible;
- z-index: 1;
- text-align: center;
- perspective: 600px;
- perspective-origin: 50% 40%;
-}
-
-.reveal .slides>section {
- -ms-perspective: 600px;
-}
-
-.reveal .slides>section,
-.reveal .slides>section>section {
- display: none;
- position: absolute;
- width: 100%;
- padding: 20px 0px;
-
- z-index: 10;
- transform-style: preserve-3d;
- transition: transform-origin 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985),
- transform 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985),
- visibility 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985),
- opacity 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985);
-}
-
-/* Global transition speed settings */
-.reveal[data-transition-speed="fast"] .slides section {
- transition-duration: 400ms;
-}
-.reveal[data-transition-speed="slow"] .slides section {
- transition-duration: 1200ms;
-}
-
-/* Slide-specific transition speed overrides */
-.reveal .slides section[data-transition-speed="fast"] {
- transition-duration: 400ms;
-}
-.reveal .slides section[data-transition-speed="slow"] {
- transition-duration: 1200ms;
-}
-
-.reveal .slides>section.stack {
- padding-top: 0;
- padding-bottom: 0;
-}
-
-.reveal .slides>section.present,
-.reveal .slides>section>section.present {
- display: block;
- z-index: 11;
- opacity: 1;
-}
-
-.reveal.center,
-.reveal.center .slides,
-.reveal.center .slides section {
- min-height: 0 !important;
-}
-
-/* Don't allow interaction with invisible slides */
-.reveal .slides>section.future,
-.reveal .slides>section>section.future,
-.reveal .slides>section.past,
-.reveal .slides>section>section.past {
- pointer-events: none;
-}
-
-.reveal.overview .slides>section,
-.reveal.overview .slides>section>section {
- pointer-events: auto;
-}
-
-.reveal .slides>section.past,
-.reveal .slides>section.future,
-.reveal .slides>section>section.past,
-.reveal .slides>section>section.future {
- opacity: 0;
-}
-
-
-/*********************************************
- * SLIDE TRANSITION
- * Aliased 'linear' for backwards compatibility
- *********************************************/
-
-.reveal.slide section,
-.reveal.linear section {
- backface-visibility: hidden;
-}
-
-.reveal .slides>section[data-transition=slide].past,
-.reveal.slide .slides>section:not([data-transition]).past,
-.reveal .slides>section[data-transition=linear].past,
-.reveal.linear .slides>section:not([data-transition]).past {
- transform: translate(-150%, 0);
-}
-.reveal .slides>section[data-transition=slide].future,
-.reveal.slide .slides>section:not([data-transition]).future,
-.reveal .slides>section[data-transition=linear].future,
-.reveal.linear .slides>section:not([data-transition]).future {
- transform: translate(150%, 0);
-}
-
-.reveal .slides>section>section[data-transition=slide].past,
-.reveal.slide .slides>section>section:not([data-transition]).past,
-.reveal .slides>section>section[data-transition=linear].past,
-.reveal.linear .slides>section>section:not([data-transition]).past {
- transform: translate(0, -150%);
-}
-.reveal .slides>section>section[data-transition=slide].future,
-.reveal.slide .slides>section>section:not([data-transition]).future,
-.reveal .slides>section>section[data-transition=linear].future,
-.reveal.linear .slides>section>section:not([data-transition]).future {
- transform: translate(0, 150%);
-}
-
-
-/*********************************************
- * CONVEX TRANSITION
- * Aliased 'default' for backwards compatibility
- *********************************************/
-
-.reveal .slides>section[data-transition=default].past,
-.reveal.default .slides>section:not([data-transition]).past,
-.reveal .slides>section[data-transition=convex].past,
-.reveal.convex .slides>section:not([data-transition]).past {
- transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0);
-}
-.reveal .slides>section[data-transition=default].future,
-.reveal.default .slides>section:not([data-transition]).future,
-.reveal .slides>section[data-transition=convex].future,
-.reveal.convex .slides>section:not([data-transition]).future {
- transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0);
-}
-
-.reveal .slides>section>section[data-transition=default].past,
-.reveal.default .slides>section>section:not([data-transition]).past,
-.reveal .slides>section>section[data-transition=convex].past,
-.reveal.convex .slides>section>section:not([data-transition]).past {
- transform: translate3d(0, -300px, 0) rotateX(70deg) translate3d(0, -300px, 0);
-}
-.reveal .slides>section>section[data-transition=default].future,
-.reveal.default .slides>section>section:not([data-transition]).future,
-.reveal .slides>section>section[data-transition=convex].future,
-.reveal.convex .slides>section>section:not([data-transition]).future {
- transform: translate3d(0, 300px, 0) rotateX(-70deg) translate3d(0, 300px, 0);
-}
-
-
-/*********************************************
- * CONCAVE TRANSITION
- *********************************************/
-
-.reveal .slides>section[data-transition=concave].past,
-.reveal.concave .slides>section:not([data-transition]).past {
- transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0);
-}
-.reveal .slides>section[data-transition=concave].future,
-.reveal.concave .slides>section:not([data-transition]).future {
- transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0);
-}
-
-.reveal .slides>section>section[data-transition=concave].past,
-.reveal.concave .slides>section>section:not([data-transition]).past {
- transform: translate3d(0, -80%, 0) rotateX(-70deg) translate3d(0, -80%, 0);
-}
-.reveal .slides>section>section[data-transition=concave].future,
-.reveal.concave .slides>section>section:not([data-transition]).future {
- transform: translate3d(0, 80%, 0) rotateX(70deg) translate3d(0, 80%, 0);
-}
-
-
-/*********************************************
- * ZOOM TRANSITION
- *********************************************/
-
-.reveal .slides>section[data-transition=zoom],
-.reveal.zoom .slides>section:not([data-transition]) {
- transition-timing-function: ease;
-}
-
-.reveal .slides>section[data-transition=zoom].past,
-.reveal.zoom .slides>section:not([data-transition]).past {
- visibility: hidden;
- transform: scale(16);
-}
-.reveal .slides>section[data-transition=zoom].future,
-.reveal.zoom .slides>section:not([data-transition]).future {
- visibility: hidden;
- transform: scale(0.2);
-}
-
-.reveal .slides>section>section[data-transition=zoom].past,
-.reveal.zoom .slides>section>section:not([data-transition]).past {
- transform: translate(0, -150%);
-}
-.reveal .slides>section>section[data-transition=zoom].future,
-.reveal.zoom .slides>section>section:not([data-transition]).future {
- transform: translate(0, 150%);
-}
-
-
-/*********************************************
- * CUBE TRANSITION
- *********************************************/
-
-.reveal.cube .slides {
- perspective: 1300px;
-}
-
-.reveal.cube .slides section {
- padding: 30px;
- min-height: 700px;
- backface-visibility: hidden;
- box-sizing: border-box;
-}
- .reveal.center.cube .slides section {
- min-height: 0;
- }
- .reveal.cube .slides section:not(.stack):before {
- content: '';
- position: absolute;
- display: block;
- width: 100%;
- height: 100%;
- left: 0;
- top: 0;
- background: rgba(0,0,0,0.1);
- border-radius: 4px;
- transform: translateZ( -20px );
- }
- .reveal.cube .slides section:not(.stack):after {
- content: '';
- position: absolute;
- display: block;
- width: 90%;
- height: 30px;
- left: 5%;
- bottom: 0;
- background: none;
- z-index: 1;
-
- border-radius: 4px;
- box-shadow: 0px 95px 25px rgba(0,0,0,0.2);
- transform: translateZ(-90px) rotateX( 65deg );
- }
-
-.reveal.cube .slides>section.stack {
- padding: 0;
- background: none;
-}
-
-.reveal.cube .slides>section.past {
- transform-origin: 100% 0%;
- transform: translate3d(-100%, 0, 0) rotateY(-90deg);
-}
-
-.reveal.cube .slides>section.future {
- transform-origin: 0% 0%;
- transform: translate3d(100%, 0, 0) rotateY(90deg);
-}
-
-.reveal.cube .slides>section>section.past {
- transform-origin: 0% 100%;
- transform: translate3d(0, -100%, 0) rotateX(90deg);
-}
-
-.reveal.cube .slides>section>section.future {
- transform-origin: 0% 0%;
- transform: translate3d(0, 100%, 0) rotateX(-90deg);
-}
-
-
-/*********************************************
- * PAGE TRANSITION
- *********************************************/
-
-.reveal.page .slides {
- perspective-origin: 0% 50%;
- perspective: 3000px;
-}
-
-.reveal.page .slides section {
- padding: 30px;
- min-height: 700px;
- box-sizing: border-box;
-}
- .reveal.page .slides section.past {
- z-index: 12;
- }
- .reveal.page .slides section:not(.stack):before {
- content: '';
- position: absolute;
- display: block;
- width: 100%;
- height: 100%;
- left: 0;
- top: 0;
- background: rgba(0,0,0,0.1);
- transform: translateZ( -20px );
- }
- .reveal.page .slides section:not(.stack):after {
- content: '';
- position: absolute;
- display: block;
- width: 90%;
- height: 30px;
- left: 5%;
- bottom: 0;
- background: none;
- z-index: 1;
-
- border-radius: 4px;
- box-shadow: 0px 95px 25px rgba(0,0,0,0.2);
-
- -webkit-transform: translateZ(-90px) rotateX( 65deg );
- }
-
-.reveal.page .slides>section.stack {
- padding: 0;
- background: none;
-}
-
-.reveal.page .slides>section.past {
- transform-origin: 0% 0%;
- transform: translate3d(-40%, 0, 0) rotateY(-80deg);
-}
-
-.reveal.page .slides>section.future {
- transform-origin: 100% 0%;
- transform: translate3d(0, 0, 0);
-}
-
-.reveal.page .slides>section>section.past {
- transform-origin: 0% 0%;
- transform: translate3d(0, -40%, 0) rotateX(80deg);
-}
-
-.reveal.page .slides>section>section.future {
- transform-origin: 0% 100%;
- transform: translate3d(0, 0, 0);
-}
-
-
-/*********************************************
- * FADE TRANSITION
- *********************************************/
-
-.reveal .slides section[data-transition=fade],
-.reveal.fade .slides section:not([data-transition]),
-.reveal.fade .slides>section>section:not([data-transition]) {
- transform: none;
- transition: opacity 0.5s;
-}
-
-
-.reveal.fade.overview .slides section,
-.reveal.fade.overview .slides>section>section {
- transition: none;
-}
-
-
-/*********************************************
- * NO TRANSITION
- *********************************************/
-
-.reveal .slides section[data-transition=none],
-.reveal.none .slides section:not([data-transition]) {
- transform: none;
- transition: none;
-}
-
-
-/*********************************************
- * OVERVIEW
- *********************************************/
-
-.reveal.overview .slides {
- perspective-origin: 50% 50%;
- perspective: 700px;
-}
-
-.reveal.overview .slides section {
- height: 700px;
- overflow: hidden;
- opacity: 1 !important;
- visibility: visible !important;
- cursor: pointer;
- background: rgba(0,0,0,0.1);
- box-sizing: border-box;
-}
-.reveal.overview .slides section,
-.reveal.overview-deactivating .slides section {
- transition: none !important;
-}
-.reveal.overview .slides section .fragment {
- opacity: 1;
-}
-.reveal.overview .slides section:after,
-.reveal.overview .slides section:before {
- display: none !important;
-}
-.reveal.overview .slides section>section {
- opacity: 1;
- cursor: pointer;
-}
- .reveal.overview .slides section:hover {
- background: rgba(0,0,0,0.3);
- }
- .reveal.overview .slides section.present {
- background: rgba(0,0,0,0.3);
- }
-.reveal.overview .slides>section.stack {
- padding: 0;
- top: 0 !important;
- background: none;
- overflow: visible;
-}
-
-
-/*********************************************
- * PAUSED MODE
- *********************************************/
-
-.reveal .pause-overlay {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- background: black;
- visibility: hidden;
- opacity: 0;
- z-index: 100;
- transition: all 1s ease;
-}
-.reveal.paused .pause-overlay {
- visibility: visible;
- opacity: 1;
-}
-
-
-/*********************************************
- * FALLBACK
- *********************************************/
-
-.no-transforms {
- overflow-y: auto;
-}
-
-.no-transforms .reveal .slides {
- position: relative;
- width: 80%;
- height: auto !important;
- top: 0;
- left: 50%;
- margin: 0;
- text-align: center;
-}
-
-.no-transforms .reveal .controls,
-.no-transforms .reveal .progress {
- display: none !important;
-}
-
-.no-transforms .reveal .slides section {
- display: block !important;
- opacity: 1 !important;
- position: relative !important;
- height: auto;
- min-height: 0;
- top: 0;
- left: -50%;
- margin: 70px 0;
- transform: none;
-}
-
-.no-transforms .reveal .slides section section {
- left: 0;
-}
-
-.reveal .no-transition,
-.reveal .no-transition * {
- transition: none !important;
-}
-
-
-/*********************************************
- * PER-SLIDE BACKGROUNDS
- *********************************************/
-
-.reveal>.backgrounds {
- position: absolute;
- width: 100%;
- height: 100%;
- top: 0;
- left: 0;
- perspective: 600px;
-}
- .reveal .slide-background {
- display: none;
- position: absolute;
- width: 100%;
- height: 100%;
- opacity: 0;
- visibility: hidden;
-
- background-color: rgba( 0, 0, 0, 0 );
- background-position: 50% 50%;
- background-repeat: no-repeat;
- background-size: cover;
-
- transition: all 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985);
- }
-
- .reveal .slide-background.stack {
- display: block;
- }
-
- .reveal .slide-background.present {
- opacity: 1;
- visibility: visible;
- }
-
- .print-pdf .reveal .slide-background {
- opacity: 1 !important;
- visibility: visible !important;
- }
-
-/* Video backgrounds */
-.reveal .slide-background video {
- position: absolute;
- width: 100%;
- height: 100%;
- max-width: none;
- max-height: none;
- top: 0;
- left: 0;
-}
-
-/* Immediate transition style */
-.reveal[data-background-transition=none]>.backgrounds .slide-background,
-.reveal>.backgrounds .slide-background[data-background-transition=none] {
- transition: none;
-}
-
-/* Slide */
-.reveal[data-background-transition=slide]>.backgrounds .slide-background,
-.reveal>.backgrounds .slide-background[data-background-transition=slide] {
- opacity: 1;
- backface-visibility: hidden;
-}
- .reveal[data-background-transition=slide]>.backgrounds .slide-background.past,
- .reveal>.backgrounds .slide-background.past[data-background-transition=slide] {
- transform: translate(-100%, 0);
- }
- .reveal[data-background-transition=slide]>.backgrounds .slide-background.future,
- .reveal>.backgrounds .slide-background.future[data-background-transition=slide] {
- transform: translate(100%, 0);
- }
-
- .reveal[data-background-transition=slide]>.backgrounds .slide-background>.slide-background.past,
- .reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=slide] {
- transform: translate(0, -100%);
- }
- .reveal[data-background-transition=slide]>.backgrounds .slide-background>.slide-background.future,
- .reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=slide] {
- transform: translate(0, 100%);
- }
-
-
-/* Convex */
-.reveal[data-background-transition=convex]>.backgrounds .slide-background.past,
-.reveal>.backgrounds .slide-background.past[data-background-transition=convex] {
- opacity: 0;
- transform: translate3d(-100%, 0, 0) rotateY(-90deg) translate3d(-100%, 0, 0);
-}
-.reveal[data-background-transition=convex]>.backgrounds .slide-background.future,
-.reveal>.backgrounds .slide-background.future[data-background-transition=convex] {
- opacity: 0;
- transform: translate3d(100%, 0, 0) rotateY(90deg) translate3d(100%, 0, 0);
-}
-
-.reveal[data-background-transition=convex]>.backgrounds .slide-background>.slide-background.past,
-.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=convex] {
- opacity: 0;
- transform: translate3d(0, -100%, 0) rotateX(90deg) translate3d(0, -100%, 0);
-}
-.reveal[data-background-transition=convex]>.backgrounds .slide-background>.slide-background.future,
-.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=convex] {
- opacity: 0;
- transform: translate3d(0, 100%, 0) rotateX(-90deg) translate3d(0, 100%, 0);
-}
-
-
-/* Concave */
-.reveal[data-background-transition=concave]>.backgrounds .slide-background.past,
-.reveal>.backgrounds .slide-background.past[data-background-transition=concave] {
- opacity: 0;
- transform: translate3d(-100%, 0, 0) rotateY(90deg) translate3d(-100%, 0, 0);
-}
-.reveal[data-background-transition=concave]>.backgrounds .slide-background.future,
-.reveal>.backgrounds .slide-background.future[data-background-transition=concave] {
- opacity: 0;
- transform: translate3d(100%, 0, 0) rotateY(-90deg) translate3d(100%, 0, 0);
-}
-
-.reveal[data-background-transition=concave]>.backgrounds .slide-background>.slide-background.past,
-.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=concave] {
- opacity: 0;
- transform: translate3d(0, -100%, 0) rotateX(-90deg) translate3d(0, -100%, 0);
-}
-.reveal[data-background-transition=concave]>.backgrounds .slide-background>.slide-background.future,
-.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=concave] {
- opacity: 0;
- transform: translate3d(0, 100%, 0) rotateX(90deg) translate3d(0, 100%, 0);
-}
-
-/* Zoom */
-.reveal[data-background-transition=zoom]>.backgrounds .slide-background,
-.reveal>.backgrounds .slide-background[data-background-transition=zoom] {
- transition-timing-function: ease;
-}
-
-.reveal[data-background-transition=zoom]>.backgrounds .slide-background.past,
-.reveal>.backgrounds .slide-background.past[data-background-transition=zoom] {
- opacity: 0;
- visibility: hidden;
- transform: scale(16);
-}
-.reveal[data-background-transition=zoom]>.backgrounds .slide-background.future,
-.reveal>.backgrounds .slide-background.future[data-background-transition=zoom] {
- opacity: 0;
- visibility: hidden;
- transform: scale(0.2);
-}
-
-.reveal[data-background-transition=zoom]>.backgrounds .slide-background>.slide-background.past,
-.reveal>.backgrounds .slide-background>.slide-background.past[data-background-transition=zoom] {
- opacity: 0;
- visibility: hidden;
- transform: scale(16);
-}
-.reveal[data-background-transition=zoom]>.backgrounds .slide-background>.slide-background.future,
-.reveal>.backgrounds .slide-background>.slide-background.future[data-background-transition=zoom] {
- opacity: 0;
- visibility: hidden;
- transform: scale(0.2);
-}
-
-
-/* Global transition speed settings */
-.reveal[data-transition-speed="fast"]>.backgrounds .slide-background {
- transition-duration: 400ms;
-}
-.reveal[data-transition-speed="slow"]>.backgrounds .slide-background {
- transition-duration: 1200ms;
-}
-
-
-/*********************************************
- * RTL SUPPORT
- *********************************************/
-
-.reveal.rtl .slides,
-.reveal.rtl .slides h1,
-.reveal.rtl .slides h2,
-.reveal.rtl .slides h3,
-.reveal.rtl .slides h4,
-.reveal.rtl .slides h5,
-.reveal.rtl .slides h6 {
- direction: rtl;
- font-family: sans-serif;
-}
-
-.reveal.rtl pre,
-.reveal.rtl code {
- direction: ltr;
-}
-
-.reveal.rtl ol,
-.reveal.rtl ul {
- text-align: right;
-}
-
-.reveal.rtl .progress span {
- float: right
-}
-
-/*********************************************
- * PARALLAX BACKGROUND
- *********************************************/
-
-.reveal.has-parallax-background .backgrounds {
- transition: all 0.8s ease;
-}
-
-/* Global transition speed settings */
-.reveal.has-parallax-background[data-transition-speed="fast"] .backgrounds {
- transition-duration: 400ms;
-}
-.reveal.has-parallax-background[data-transition-speed="slow"] .backgrounds {
- transition-duration: 1200ms;
-}
-
-
-/*********************************************
- * LINK PREVIEW OVERLAY
- *********************************************/
-
-.reveal .overlay {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: 1000;
- background: rgba( 0, 0, 0, 0.9 );
- opacity: 0;
- visibility: hidden;
- transition: all 0.3s ease;
-}
- .reveal .overlay.visible {
- opacity: 1;
- visibility: visible;
- }
-
- .reveal .overlay .spinner {
- position: absolute;
- display: block;
- top: 50%;
- left: 50%;
- width: 32px;
- height: 32px;
- margin: -16px 0 0 -16px;
- z-index: 10;
- background-image: url(data:image/gif;base64,R0lGODlhIAAgAPMAAJmZmf%2F%2F%2F6%2Bvr8nJybW1tcDAwOjo6Nvb26ioqKOjo7Ozs%2FLy8vz8%2FAAAAAAAAAAAACH%2FC05FVFNDQVBFMi4wAwEAAAAh%2FhpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh%2BQQJCgAAACwAAAAAIAAgAAAE5xDISWlhperN52JLhSSdRgwVo1ICQZRUsiwHpTJT4iowNS8vyW2icCF6k8HMMBkCEDskxTBDAZwuAkkqIfxIQyhBQBFvAQSDITM5VDW6XNE4KagNh6Bgwe60smQUB3d4Rz1ZBApnFASDd0hihh12BkE9kjAJVlycXIg7CQIFA6SlnJ87paqbSKiKoqusnbMdmDC2tXQlkUhziYtyWTxIfy6BE8WJt5YJvpJivxNaGmLHT0VnOgSYf0dZXS7APdpB309RnHOG5gDqXGLDaC457D1zZ%2FV%2FnmOM82XiHRLYKhKP1oZmADdEAAAh%2BQQJCgAAACwAAAAAIAAgAAAE6hDISWlZpOrNp1lGNRSdRpDUolIGw5RUYhhHukqFu8DsrEyqnWThGvAmhVlteBvojpTDDBUEIFwMFBRAmBkSgOrBFZogCASwBDEY%2FCZSg7GSE0gSCjQBMVG023xWBhklAnoEdhQEfyNqMIcKjhRsjEdnezB%2BA4k8gTwJhFuiW4dokXiloUepBAp5qaKpp6%2BHo7aWW54wl7obvEe0kRuoplCGepwSx2jJvqHEmGt6whJpGpfJCHmOoNHKaHx61WiSR92E4lbFoq%2BB6QDtuetcaBPnW6%2BO7wDHpIiK9SaVK5GgV543tzjgGcghAgAh%2BQQJCgAAACwAAAAAIAAgAAAE7hDISSkxpOrN5zFHNWRdhSiVoVLHspRUMoyUakyEe8PTPCATW9A14E0UvuAKMNAZKYUZCiBMuBakSQKG8G2FzUWox2AUtAQFcBKlVQoLgQReZhQlCIJesQXI5B0CBnUMOxMCenoCfTCEWBsJColTMANldx15BGs8B5wlCZ9Po6OJkwmRpnqkqnuSrayqfKmqpLajoiW5HJq7FL1Gr2mMMcKUMIiJgIemy7xZtJsTmsM4xHiKv5KMCXqfyUCJEonXPN2rAOIAmsfB3uPoAK%2B%2BG%2Bw48edZPK%2BM6hLJpQg484enXIdQFSS1u6UhksENEQAAIfkECQoAAAAsAAAAACAAIAAABOcQyEmpGKLqzWcZRVUQnZYg1aBSh2GUVEIQ2aQOE%2BG%2BcD4ntpWkZQj1JIiZIogDFFyHI0UxQwFugMSOFIPJftfVAEoZLBbcLEFhlQiqGp1Vd140AUklUN3eCA51C1EWMzMCezCBBmkxVIVHBWd3HHl9JQOIJSdSnJ0TDKChCwUJjoWMPaGqDKannasMo6WnM562R5YluZRwur0wpgqZE7NKUm%2BFNRPIhjBJxKZteWuIBMN4zRMIVIhffcgojwCF117i4nlLnY5ztRLsnOk%2BaV%2BoJY7V7m76PdkS4trKcdg0Zc0tTcKkRAAAIfkECQoAAAAsAAAAACAAIAAABO4QyEkpKqjqzScpRaVkXZWQEximw1BSCUEIlDohrft6cpKCk5xid5MNJTaAIkekKGQkWyKHkvhKsR7ARmitkAYDYRIbUQRQjWBwJRzChi9CRlBcY1UN4g0%2FVNB0AlcvcAYHRyZPdEQFYV8ccwR5HWxEJ02YmRMLnJ1xCYp0Y5idpQuhopmmC2KgojKasUQDk5BNAwwMOh2RtRq5uQuPZKGIJQIGwAwGf6I0JXMpC8C7kXWDBINFMxS4DKMAWVWAGYsAdNqW5uaRxkSKJOZKaU3tPOBZ4DuK2LATgJhkPJMgTwKCdFjyPHEnKxFCDhEAACH5BAkKAAAALAAAAAAgACAAAATzEMhJaVKp6s2nIkolIJ2WkBShpkVRWqqQrhLSEu9MZJKK9y1ZrqYK9WiClmvoUaF8gIQSNeF1Er4MNFn4SRSDARWroAIETg1iVwuHjYB1kYc1mwruwXKC9gmsJXliGxc%2BXiUCby9ydh1sOSdMkpMTBpaXBzsfhoc5l58Gm5yToAaZhaOUqjkDgCWNHAULCwOLaTmzswadEqggQwgHuQsHIoZCHQMMQgQGubVEcxOPFAcMDAYUA85eWARmfSRQCdcMe0zeP1AAygwLlJtPNAAL19DARdPzBOWSm1brJBi45soRAWQAAkrQIykShQ9wVhHCwCQCACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiRMDjI0Fd30%2FiI2UA5GSS5UDj2l6NoqgOgN4gksEBgYFf0FDqKgHnyZ9OX8HrgYHdHpcHQULXAS2qKpENRg7eAMLC7kTBaixUYFkKAzWAAnLC7FLVxLWDBLKCwaKTULgEwbLA4hJtOkSBNqITT3xEgfLpBtzE%2FjiuL04RGEBgwWhShRgQExHBAAh%2BQQJCgAAACwAAAAAIAAgAAAE7xDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfZiCqGk5dTESJeaOAlClzsJsqwiJwiqnFrb2nS9kmIcgEsjQydLiIlHehhpejaIjzh9eomSjZR%2BipslWIRLAgMDOR2DOqKogTB9pCUJBagDBXR6XB0EBkIIsaRsGGMMAxoDBgYHTKJiUYEGDAzHC9EACcUGkIgFzgwZ0QsSBcXHiQvOwgDdEwfFs0sDzt4S6BK4xYjkDOzn0unFeBzOBijIm1Dgmg5YFQwsCMjp1oJ8LyIAACH5BAkKAAAALAAAAAAgACAAAATwEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GGl6NoiPOH16iZKNlH6KmyWFOggHhEEvAwwMA0N9GBsEC6amhnVcEwavDAazGwIDaH1ipaYLBUTCGgQDA8NdHz0FpqgTBwsLqAbWAAnIA4FWKdMLGdYGEgraigbT0OITBcg5QwPT4xLrROZL6AuQAPUS7bxLpoWidY0JtxLHKhwwMJBTHgPKdEQAACH5BAkKAAAALAAAAAAgACAAAATrEMhJaVKp6s2nIkqFZF2VIBWhUsJaTokqUCoBq%2BE71SRQeyqUToLA7VxF0JDyIQh%2FMVVPMt1ECZlfcjZJ9mIKoaTl1MRIl5o4CUKXOwmyrCInCKqcWtvadL2SYhyASyNDJ0uIiUd6GAULDJCRiXo1CpGXDJOUjY%2BYip9DhToJA4RBLwMLCwVDfRgbBAaqqoZ1XBMHswsHtxtFaH1iqaoGNgAIxRpbFAgfPQSqpbgGBqUD1wBXeCYp1AYZ19JJOYgH1KwA4UBvQwXUBxPqVD9L3sbp2BNk2xvvFPJd%2BMFCN6HAAIKgNggY0KtEBAAh%2BQQJCgAAACwAAAAAIAAgAAAE6BDISWlSqerNpyJKhWRdlSAVoVLCWk6JKlAqAavhO9UkUHsqlE6CwO1cRdCQ8iEIfzFVTzLdRAmZX3I2SfYIDMaAFdTESJeaEDAIMxYFqrOUaNW4E4ObYcCXaiBVEgULe0NJaxxtYksjh2NLkZISgDgJhHthkpU4mW6blRiYmZOlh4JWkDqILwUGBnE6TYEbCgevr0N1gH4At7gHiRpFaLNrrq8HNgAJA70AWxQIH1%2BvsYMDAzZQPC9VCNkDWUhGkuE5PxJNwiUK4UfLzOlD4WvzAHaoG9nxPi5d%2BjYUqfAhhykOFwJWiAAAIfkECQoAAAAsAAAAACAAIAAABPAQyElpUqnqzaciSoVkXVUMFaFSwlpOCcMYlErAavhOMnNLNo8KsZsMZItJEIDIFSkLGQoQTNhIsFehRww2CQLKF0tYGKYSg%2BygsZIuNqJksKgbfgIGepNo2cIUB3V1B3IvNiBYNQaDSTtfhhx0CwVPI0UJe0%2Bbm4g5VgcGoqOcnjmjqDSdnhgEoamcsZuXO1aWQy8KAwOAuTYYGwi7w5h%2BKr0SJ8MFihpNbx%2B4Erq7BYBuzsdiH1jCAzoSfl0rVirNbRXlBBlLX%2BBP0XJLAPGzTkAuAOqb0WT5AH7OcdCm5B8TgRwSRKIHQtaLCwg1RAAAOwAAAAAAAAAAAA%3D%3D);
-
- visibility: visible;
- opacity: 0.6;
- transition: all 0.3s ease;
- }
-
- .reveal .overlay header {
- position: absolute;
- left: 0;
- top: 0;
- width: 100%;
- height: 40px;
- z-index: 2;
- border-bottom: 1px solid #222;
- }
- .reveal .overlay header a {
- display: inline-block;
- width: 40px;
- height: 40px;
- padding: 0 10px;
- float: right;
- opacity: 0.6;
-
- box-sizing: border-box;
- }
- .reveal .overlay header a:hover {
- opacity: 1;
- }
- .reveal .overlay header a .icon {
- display: inline-block;
- width: 20px;
- height: 20px;
-
- background-position: 50% 50%;
- background-size: 100%;
- background-repeat: no-repeat;
- }
- .reveal .overlay header a.close .icon {
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABkklEQVRYR8WX4VHDMAxG6wnoJrABZQPYBCaBTWAD2g1gE5gg6OOsXuxIlr40d81dfrSJ9V4c2VLK7spHuTJ/5wpM07QXuXc5X0opX2tEJcadjHuV80li/FgxTIEK/5QBCICBD6xEhSMGHgQPgBgLiYVAB1dpSqKDawxTohFw4JSEA3clzgIBPCURwE2JucBR7rhPJJv5OpJwDX+SfDjgx1wACQeJG1aChP9K/IMmdZ8DtESV1WyP3Bt4MwM6sj4NMxMYiqUWHQu4KYA/SYkIjOsm3BXYWMKFDwU2khjCQ4ELJUJ4SmClRArOCmSXGuKma0fYD5CbzHxFpCSGAhfAVSSUGDUk2BWZaff2g6GE15BsBQ9nwmpIGDiyHQddwNTMKkbZaf9fajXQca1EX44puJZUsnY0ObGmITE3GVLCbEhQUjGVt146j6oasWN+49Vph2w1pZ5EansNZqKBm1txbU57iRRcZ86RWMDdWtBJUHBHwoQPi1GV+JCbntmvok7iTX4/Up9mgyTc/FJYDTcndgH/AA5A/CHsyEkVAAAAAElFTkSuQmCC);
- }
- .reveal .overlay header a.external .icon {
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAcElEQVRYR+2WSQoAIQwEzf8f7XiOMkUQxUPlGkM3hVmiQfQR9GYnH1SsAQlI4DiBqkCMoNb9y2e90IAEJPAcgdznU9+engMaeJ7Azh5Y1U67gAho4DqBqmB1buAf0MB1AlVBek83ZPkmJMGc1wAR+AAqod/B97TRpQAAAABJRU5ErkJggg==);
- }
-
- .reveal .overlay .viewport {
- position: absolute;
- top: 40px;
- right: 0;
- bottom: 0;
- left: 0;
- }
-
- .reveal .overlay.overlay-preview .viewport iframe {
- width: 100%;
- height: 100%;
- max-width: 100%;
- max-height: 100%;
- border: 0;
-
- opacity: 0;
- visibility: hidden;
- transition: all 0.3s ease;
- }
-
- .reveal .overlay.overlay-preview.loaded .viewport iframe {
- opacity: 1;
- visibility: visible;
- }
-
- .reveal .overlay.overlay-preview.loaded .spinner {
- opacity: 0;
- visibility: hidden;
- transform: scale(0.2);
- }
-
- .reveal .overlay.overlay-help .viewport {
- overflow: auto;
- color: #fff;
- }
-
- .reveal .overlay.overlay-help .viewport .viewport-inner {
- width: 600px;
- margin: 0 auto;
- padding: 60px;
- text-align: center;
- letter-spacing: normal;
- }
-
- .reveal .overlay.overlay-help .viewport .viewport-inner .title {
- font-size: 20px;
- }
-
- .reveal .overlay.overlay-help .viewport .viewport-inner table {
- border: 1px solid #fff;
- border-collapse: collapse;
- font-size: 14px;
- }
-
- .reveal .overlay.overlay-help .viewport .viewport-inner table th,
- .reveal .overlay.overlay-help .viewport .viewport-inner table td {
- width: 200px;
- padding: 10px;
- border: 1px solid #fff;
- vertical-align: middle;
- }
-
- .reveal .overlay.overlay-help .viewport .viewport-inner table th {
- padding-top: 20px;
- padding-bottom: 20px;
- }
-
-
-
-/*********************************************
- * PLAYBACK COMPONENT
- *********************************************/
-
-.reveal .playback {
- position: fixed;
- left: 15px;
- bottom: 15px;
- z-index: 30;
- cursor: pointer;
- transition: all 400ms ease;
-}
-
-.reveal.overview .playback {
- opacity: 0;
- visibility: hidden;
-}
-
-
-/*********************************************
- * ROLLING LINKS
- *********************************************/
-
-.reveal .roll {
- display: inline-block;
- line-height: 1.2;
- overflow: hidden;
-
- vertical-align: top;
- perspective: 400px;
- perspective-origin: 50% 50%;
-}
- .reveal .roll:hover {
- background: none;
- text-shadow: none;
- }
-.reveal .roll span {
- display: block;
- position: relative;
- padding: 0 2px;
-
- pointer-events: none;
- transition: all 400ms ease;
- transform-origin: 50% 0%;
- transform-style: preserve-3d;
- backface-visibility: hidden;
-}
- .reveal .roll:hover span {
- background: rgba(0,0,0,0.5);
- transform: translate3d( 0px, 0px, -45px ) rotateX( 90deg );
- }
-.reveal .roll span:after {
- content: attr(data-title);
-
- display: block;
- position: absolute;
- left: 0;
- top: 0;
- padding: 0 2px;
- backface-visibility: hidden;
- transform-origin: 50% 0%;
- transform: translate3d( 0px, 110%, 0px ) rotateX( -90deg );
-}
-
-
-/*********************************************
- * SPEAKER NOTES
- *********************************************/
-
-.reveal aside.notes {
- display: none;
-}
-
-
-/*********************************************
- * ZOOM PLUGIN
- *********************************************/
-
-.zoomed .reveal *,
-.zoomed .reveal *:before,
-.zoomed .reveal *:after {
- backface-visibility: visible !important;
-}
-
-.zoomed .reveal .progress,
-.zoomed .reveal .controls {
- opacity: 0;
-}
-
-.zoomed .reveal .roll span {
- background: none;
-}
-
-.zoomed .reveal .roll span:after {
- visibility: hidden;
-}
-
-
diff --git a/presentation/css/theme/README.md b/presentation/css/theme/README.md
deleted file mode 100644
index 90dc149c6..000000000
--- a/presentation/css/theme/README.md
+++ /dev/null
@@ -1,25 +0,0 @@
-## Dependencies
-
-Themes are written using Sass to keep things modular and reduce the need for repeated selectors across files. Make sure that you have the reveal.js development environment including the Grunt dependencies installed before proceding: https://github.com/hakimel/reveal.js#full-setup
-
-You also need to install Ruby and then Sass (with `gem install sass`).
-
-## Creating a Theme
-
-To create your own theme, start by duplicating any ```.scss``` file in [/css/theme/source](https://github.com/hakimel/reveal.js/blob/master/css/theme/source) and adding it to the compilation list in the [Gruntfile](https://github.com/hakimel/reveal.js/blob/master/Gruntfile.js).
-
-Each theme file does four things in the following order:
-
-1. **Include [/css/theme/template/mixins.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/mixins.scss)**
-Shared utility functions.
-
-2. **Include [/css/theme/template/settings.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/settings.scss)**
-Declares a set of custom variables that the template file (step 4) expects. Can be overridden in step 3.
-
-3. **Override**
-This is where you override the default theme. Either by specifying variables (see [settings.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/settings.scss) for reference) or by adding full selectors with hardcoded styles.
-
-4. **Include [/css/theme/template/theme.scss](https://github.com/hakimel/reveal.js/blob/master/css/theme/template/theme.scss)**
-The template theme file which will generate final CSS output based on the currently defined variables.
-
-When you are done, run `grunt css-themes` to compile the Sass file to CSS and you are ready to use your new theme.
diff --git a/presentation/css/theme/beige.css b/presentation/css/theme/beige.css
deleted file mode 100644
index 514427cdc..000000000
--- a/presentation/css/theme/beige.css
+++ /dev/null
@@ -1,265 +0,0 @@
-@import url(../../lib/font/league-gothic/league-gothic.css);
-@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);
-/**
- * Beige theme for reveal.js.
- *
- * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
- */
-/*********************************************
- * GLOBAL STYLES
- *********************************************/
-body {
- background: #f7f2d3;
- background: -moz-radial-gradient(center, circle cover, #ffffff 0%, #f7f2d3 100%);
- background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, #ffffff), color-stop(100%, #f7f2d3));
- background: -webkit-radial-gradient(center, circle cover, #ffffff 0%, #f7f2d3 100%);
- background: -o-radial-gradient(center, circle cover, #ffffff 0%, #f7f2d3 100%);
- background: -ms-radial-gradient(center, circle cover, #ffffff 0%, #f7f2d3 100%);
- background: radial-gradient(center, circle cover, #ffffff 0%, #f7f2d3 100%);
- background-color: #f7f3de; }
-
-.reveal {
- font-family: 'Lato', sans-serif;
- font-size: 36px;
- font-weight: normal;
- color: #333; }
-
-::selection {
- color: #fff;
- background: rgba(79, 64, 28, 0.99);
- text-shadow: none; }
-
-.reveal .slides > section, .reveal .slides > section > section {
- line-height: 1.3;
- font-weight: inherit; }
-
-/*********************************************
- * HEADERS
- *********************************************/
-.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {
- margin: 0 0 20px 0;
- color: #333;
- font-family: 'League Gothic', Impact, sans-serif;
- font-weight: normal;
- line-height: 1.2;
- letter-spacing: normal;
- text-transform: uppercase;
- text-shadow: none;
- word-wrap: break-word; }
-
-.reveal h1 {
- font-size: 3.77em; }
-
-.reveal h2 {
- font-size: 2.11em; }
-
-.reveal h3 {
- font-size: 1.55em; }
-
-.reveal h4 {
- font-size: 1em; }
-
-.reveal h1 {
- text-shadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0, 0, 0, 0.1), 0 0 5px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.3), 0 3px 5px rgba(0, 0, 0, 0.2), 0 5px 10px rgba(0, 0, 0, 0.25), 0 20px 20px rgba(0, 0, 0, 0.15); }
-
-/*********************************************
- * OTHER
- *********************************************/
-.reveal p {
- margin: 20px 0;
- line-height: 1.3; }
-
-/* Ensure certain elements are never larger than the slide itself */
-.reveal img, .reveal video, .reveal iframe {
- max-width: 95%;
- max-height: 95%; }
-
-.reveal strong, .reveal b {
- font-weight: bold; }
-
-.reveal em {
- font-style: italic; }
-
-.reveal ol, .reveal dl, .reveal ul {
- display: inline-block;
- text-align: left;
- margin: 0 0 0 1em; }
-
-.reveal ol {
- list-style-type: decimal; }
-
-.reveal ul {
- list-style-type: disc; }
-
-.reveal ul ul {
- list-style-type: square; }
-
-.reveal ul ul ul {
- list-style-type: circle; }
-
-.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul {
- display: block;
- margin-left: 40px; }
-
-.reveal dt {
- font-weight: bold; }
-
-.reveal dd {
- margin-left: 40px; }
-
-.reveal q, .reveal blockquote {
- quotes: none; }
-
-.reveal blockquote {
- display: block;
- position: relative;
- width: 70%;
- margin: 20px auto;
- padding: 5px;
- font-style: italic;
- background: rgba(255, 255, 255, 0.05);
- box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); }
-
-.reveal blockquote p:first-child, .reveal blockquote p:last-child {
- display: inline-block; }
-
-.reveal q {
- font-style: italic; }
-
-.reveal pre {
- display: block;
- position: relative;
- width: 90%;
- margin: 20px auto;
- text-align: left;
- font-size: 0.55em;
- font-family: monospace;
- line-height: 1.2em;
- word-wrap: break-word;
- box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
-
-.reveal code {
- font-family: monospace; }
-
-.reveal pre code {
- display: block;
- padding: 5px;
- overflow: auto;
- max-height: 400px;
- word-wrap: normal;
- background: #3F3F3F;
- color: #DCDCDC; }
-
-.reveal table {
- margin: auto;
- border-collapse: collapse;
- border-spacing: 0; }
-
-.reveal table th {
- font-weight: bold; }
-
-.reveal table th, .reveal table td {
- text-align: left;
- padding: 0.2em 0.5em 0.2em 0.5em;
- border-bottom: 1px solid; }
-
-.reveal table tr:last-child td {
- border-bottom: none; }
-
-.reveal sup {
- vertical-align: super; }
-
-.reveal sub {
- vertical-align: sub; }
-
-.reveal small {
- display: inline-block;
- font-size: 0.6em;
- line-height: 1.2em;
- vertical-align: top; }
-
-.reveal small * {
- vertical-align: top; }
-
-/*********************************************
- * LINKS
- *********************************************/
-.reveal a {
- color: #8b743d;
- text-decoration: none;
- -webkit-transition: color 0.15s ease;
- -moz-transition: color 0.15s ease;
- transition: color 0.15s ease; }
-
-.reveal a:hover {
- color: #c0a76e;
- text-shadow: none;
- border: none; }
-
-.reveal .roll span:after {
- color: #fff;
- background: #564726; }
-
-/*********************************************
- * IMAGES
- *********************************************/
-.reveal section img {
- margin: 15px 0px;
- background: rgba(255, 255, 255, 0.12);
- border: 4px solid #333;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); }
-
-.reveal a img {
- -webkit-transition: all 0.15s linear;
- -moz-transition: all 0.15s linear;
- transition: all 0.15s linear; }
-
-.reveal a:hover img {
- background: rgba(255, 255, 255, 0.2);
- border-color: #8b743d;
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); }
-
-/*********************************************
- * NAVIGATION CONTROLS
- *********************************************/
-.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled {
- border-right-color: #8b743d; }
-
-.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled {
- border-left-color: #8b743d; }
-
-.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled {
- border-bottom-color: #8b743d; }
-
-.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled {
- border-top-color: #8b743d; }
-
-.reveal .controls div.navigate-left.enabled:hover {
- border-right-color: #c0a76e; }
-
-.reveal .controls div.navigate-right.enabled:hover {
- border-left-color: #c0a76e; }
-
-.reveal .controls div.navigate-up.enabled:hover {
- border-bottom-color: #c0a76e; }
-
-.reveal .controls div.navigate-down.enabled:hover {
- border-top-color: #c0a76e; }
-
-/*********************************************
- * PROGRESS BAR
- *********************************************/
-.reveal .progress {
- background: rgba(0, 0, 0, 0.2); }
-
-.reveal .progress span {
- background: #8b743d;
- -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
-
-/*********************************************
- * SLIDE NUMBER
- *********************************************/
-.reveal .slide-number {
- color: #8b743d; }
diff --git a/presentation/css/theme/black.css b/presentation/css/theme/black.css
deleted file mode 100644
index 7d3bee4f7..000000000
--- a/presentation/css/theme/black.css
+++ /dev/null
@@ -1,261 +0,0 @@
-@import url(../../lib/font/source-sans-pro/source-sans-pro.css);
-/**
- * Black theme for reveal.js. This is the opposite of the 'white' theme.
- *
- * Copyright (C) 2015 Hakim El Hattab, http://hakim.se
- */
-section.has-light-background, section.has-light-background h1, section.has-light-background h2, section.has-light-background h3, section.has-light-background h4, section.has-light-background h5, section.has-light-background h6 {
- color: #222; }
-
-/*********************************************
- * GLOBAL STYLES
- *********************************************/
-body {
- background: #222;
- background-color: #222; }
-
-.reveal {
- font-family: 'Source Sans Pro', Helvetica, sans-serif;
- font-size: 38px;
- font-weight: normal;
- color: #fff; }
-
-::selection {
- color: #fff;
- background: #bee4fd;
- text-shadow: none; }
-
-.reveal .slides > section, .reveal .slides > section > section {
- line-height: 1.3;
- font-weight: inherit; }
-
-/*********************************************
- * HEADERS
- *********************************************/
-.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {
- margin: 0 0 20px 0;
- color: #fff;
- font-family: 'Source Sans Pro', Helvetica, sans-serif;
- font-weight: 600;
- line-height: 1.2;
- letter-spacing: normal;
- text-transform: uppercase;
- text-shadow: none;
- word-wrap: break-word; }
-
-.reveal h1 {
- font-size: 2.5em; }
-
-.reveal h2 {
- font-size: 1.6em; }
-
-.reveal h3 {
- font-size: 1.3em; }
-
-.reveal h4 {
- font-size: 1em; }
-
-.reveal h1 {
- text-shadow: none; }
-
-/*********************************************
- * OTHER
- *********************************************/
-.reveal p {
- margin: 20px 0;
- line-height: 1.3; }
-
-/* Ensure certain elements are never larger than the slide itself */
-.reveal img, .reveal video, .reveal iframe {
- max-width: 95%;
- max-height: 95%; }
-
-.reveal strong, .reveal b {
- font-weight: bold; }
-
-.reveal em {
- font-style: italic; }
-
-.reveal ol, .reveal dl, .reveal ul {
- display: inline-block;
- text-align: left;
- margin: 0 0 0 1em; }
-
-.reveal ol {
- list-style-type: decimal; }
-
-.reveal ul {
- list-style-type: disc; }
-
-.reveal ul ul {
- list-style-type: square; }
-
-.reveal ul ul ul {
- list-style-type: circle; }
-
-.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul {
- display: block;
- margin-left: 40px; }
-
-.reveal dt {
- font-weight: bold; }
-
-.reveal dd {
- margin-left: 40px; }
-
-.reveal q, .reveal blockquote {
- quotes: none; }
-
-.reveal blockquote {
- display: block;
- position: relative;
- width: 70%;
- margin: 20px auto;
- padding: 5px;
- font-style: italic;
- background: rgba(255, 255, 255, 0.05);
- box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); }
-
-.reveal blockquote p:first-child, .reveal blockquote p:last-child {
- display: inline-block; }
-
-.reveal q {
- font-style: italic; }
-
-.reveal pre {
- display: block;
- position: relative;
- width: 90%;
- margin: 20px auto;
- text-align: left;
- font-size: 0.55em;
- font-family: monospace;
- line-height: 1.2em;
- word-wrap: break-word;
- box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
-
-.reveal code {
- font-family: monospace; }
-
-.reveal pre code {
- display: block;
- padding: 5px;
- overflow: auto;
- max-height: 400px;
- word-wrap: normal;
- background: #3F3F3F;
- color: #DCDCDC; }
-
-.reveal table {
- margin: auto;
- border-collapse: collapse;
- border-spacing: 0; }
-
-.reveal table th {
- font-weight: bold; }
-
-.reveal table th, .reveal table td {
- text-align: left;
- padding: 0.2em 0.5em 0.2em 0.5em;
- border-bottom: 1px solid; }
-
-.reveal table tr:last-child td {
- border-bottom: none; }
-
-.reveal sup {
- vertical-align: super; }
-
-.reveal sub {
- vertical-align: sub; }
-
-.reveal small {
- display: inline-block;
- font-size: 0.6em;
- line-height: 1.2em;
- vertical-align: top; }
-
-.reveal small * {
- vertical-align: top; }
-
-/*********************************************
- * LINKS
- *********************************************/
-.reveal a {
- color: #42affa;
- text-decoration: none;
- -webkit-transition: color 0.15s ease;
- -moz-transition: color 0.15s ease;
- transition: color 0.15s ease; }
-
-.reveal a:hover {
- color: #8dcffc;
- text-shadow: none;
- border: none; }
-
-.reveal .roll span:after {
- color: #fff;
- background: #068ee9; }
-
-/*********************************************
- * IMAGES
- *********************************************/
-.reveal section img {
- margin: 15px 0px;
- background: rgba(255, 255, 255, 0.12);
- border: 4px solid #fff;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); }
-
-.reveal a img {
- -webkit-transition: all 0.15s linear;
- -moz-transition: all 0.15s linear;
- transition: all 0.15s linear; }
-
-.reveal a:hover img {
- background: rgba(255, 255, 255, 0.2);
- border-color: #42affa;
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); }
-
-/*********************************************
- * NAVIGATION CONTROLS
- *********************************************/
-.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled {
- border-right-color: #42affa; }
-
-.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled {
- border-left-color: #42affa; }
-
-.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled {
- border-bottom-color: #42affa; }
-
-.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled {
- border-top-color: #42affa; }
-
-.reveal .controls div.navigate-left.enabled:hover {
- border-right-color: #8dcffc; }
-
-.reveal .controls div.navigate-right.enabled:hover {
- border-left-color: #8dcffc; }
-
-.reveal .controls div.navigate-up.enabled:hover {
- border-bottom-color: #8dcffc; }
-
-.reveal .controls div.navigate-down.enabled:hover {
- border-top-color: #8dcffc; }
-
-/*********************************************
- * PROGRESS BAR
- *********************************************/
-.reveal .progress {
- background: rgba(0, 0, 0, 0.2); }
-
-.reveal .progress span {
- background: #42affa;
- -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
-
-/*********************************************
- * SLIDE NUMBER
- *********************************************/
-.reveal .slide-number {
- color: #42affa; }
diff --git a/presentation/css/theme/blood.css b/presentation/css/theme/blood.css
deleted file mode 100644
index 768d2a367..000000000
--- a/presentation/css/theme/blood.css
+++ /dev/null
@@ -1,291 +0,0 @@
-@import url(https://fonts.googleapis.com/css?family=Ubuntu:300,700,300italic,700italic);
-/**
- * Blood theme for reveal.js
- * Author: Walther http://github.com/Walther
- *
- * Designed to be used with highlight.js theme
- * "monokai_sublime.css" available from
- * https://github.com/isagalaev/highlight.js/
- *
- * For other themes, change $codeBackground accordingly.
- *
- */
-/*********************************************
- * GLOBAL STYLES
- *********************************************/
-body {
- background: #222;
- background: -moz-radial-gradient(center, circle cover, #626262 0%, #222 100%);
- background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, #626262), color-stop(100%, #222));
- background: -webkit-radial-gradient(center, circle cover, #626262 0%, #222 100%);
- background: -o-radial-gradient(center, circle cover, #626262 0%, #222 100%);
- background: -ms-radial-gradient(center, circle cover, #626262 0%, #222 100%);
- background: radial-gradient(center, circle cover, #626262 0%, #222 100%);
- background-color: #2b2b2b; }
-
-.reveal {
- font-family: Ubuntu, 'sans-serif';
- font-size: 36px;
- font-weight: normal;
- color: #eee; }
-
-::selection {
- color: #fff;
- background: #a23;
- text-shadow: none; }
-
-.reveal .slides > section, .reveal .slides > section > section {
- line-height: 1.3;
- font-weight: inherit; }
-
-/*********************************************
- * HEADERS
- *********************************************/
-.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {
- margin: 0 0 20px 0;
- color: #eee;
- font-family: Ubuntu, 'sans-serif';
- font-weight: normal;
- line-height: 1.2;
- letter-spacing: normal;
- text-transform: uppercase;
- text-shadow: 2px 2px 2px #222;
- word-wrap: break-word; }
-
-.reveal h1 {
- font-size: 3.77em; }
-
-.reveal h2 {
- font-size: 2.11em; }
-
-.reveal h3 {
- font-size: 1.55em; }
-
-.reveal h4 {
- font-size: 1em; }
-
-.reveal h1 {
- text-shadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0, 0, 0, 0.1), 0 0 5px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.3), 0 3px 5px rgba(0, 0, 0, 0.2), 0 5px 10px rgba(0, 0, 0, 0.25), 0 20px 20px rgba(0, 0, 0, 0.15); }
-
-/*********************************************
- * OTHER
- *********************************************/
-.reveal p {
- margin: 20px 0;
- line-height: 1.3; }
-
-/* Ensure certain elements are never larger than the slide itself */
-.reveal img, .reveal video, .reveal iframe {
- max-width: 95%;
- max-height: 95%; }
-
-.reveal strong, .reveal b {
- font-weight: bold; }
-
-.reveal em {
- font-style: italic; }
-
-.reveal ol, .reveal dl, .reveal ul {
- display: inline-block;
- text-align: left;
- margin: 0 0 0 1em; }
-
-.reveal ol {
- list-style-type: decimal; }
-
-.reveal ul {
- list-style-type: disc; }
-
-.reveal ul ul {
- list-style-type: square; }
-
-.reveal ul ul ul {
- list-style-type: circle; }
-
-.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul {
- display: block;
- margin-left: 40px; }
-
-.reveal dt {
- font-weight: bold; }
-
-.reveal dd {
- margin-left: 40px; }
-
-.reveal q, .reveal blockquote {
- quotes: none; }
-
-.reveal blockquote {
- display: block;
- position: relative;
- width: 70%;
- margin: 20px auto;
- padding: 5px;
- font-style: italic;
- background: rgba(255, 255, 255, 0.05);
- box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); }
-
-.reveal blockquote p:first-child, .reveal blockquote p:last-child {
- display: inline-block; }
-
-.reveal q {
- font-style: italic; }
-
-.reveal pre {
- display: block;
- position: relative;
- width: 90%;
- margin: 20px auto;
- text-align: left;
- font-size: 0.55em;
- font-family: monospace;
- line-height: 1.2em;
- word-wrap: break-word;
- box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
-
-.reveal code {
- font-family: monospace; }
-
-.reveal pre code {
- display: block;
- padding: 5px;
- overflow: auto;
- max-height: 400px;
- word-wrap: normal;
- background: #3F3F3F;
- color: #DCDCDC; }
-
-.reveal table {
- margin: auto;
- border-collapse: collapse;
- border-spacing: 0; }
-
-.reveal table th {
- font-weight: bold; }
-
-.reveal table th, .reveal table td {
- text-align: left;
- padding: 0.2em 0.5em 0.2em 0.5em;
- border-bottom: 1px solid; }
-
-.reveal table tr:last-child td {
- border-bottom: none; }
-
-.reveal sup {
- vertical-align: super; }
-
-.reveal sub {
- vertical-align: sub; }
-
-.reveal small {
- display: inline-block;
- font-size: 0.6em;
- line-height: 1.2em;
- vertical-align: top; }
-
-.reveal small * {
- vertical-align: top; }
-
-/*********************************************
- * LINKS
- *********************************************/
-.reveal a {
- color: #a23;
- text-decoration: none;
- -webkit-transition: color 0.15s ease;
- -moz-transition: color 0.15s ease;
- transition: color 0.15s ease; }
-
-.reveal a:hover {
- color: #dd5567;
- text-shadow: none;
- border: none; }
-
-.reveal .roll span:after {
- color: #fff;
- background: #6a1521; }
-
-/*********************************************
- * IMAGES
- *********************************************/
-.reveal section img {
- margin: 15px 0px;
- background: rgba(255, 255, 255, 0.12);
- border: 4px solid #eee;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); }
-
-.reveal a img {
- -webkit-transition: all 0.15s linear;
- -moz-transition: all 0.15s linear;
- transition: all 0.15s linear; }
-
-.reveal a:hover img {
- background: rgba(255, 255, 255, 0.2);
- border-color: #a23;
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); }
-
-/*********************************************
- * NAVIGATION CONTROLS
- *********************************************/
-.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled {
- border-right-color: #a23; }
-
-.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled {
- border-left-color: #a23; }
-
-.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled {
- border-bottom-color: #a23; }
-
-.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled {
- border-top-color: #a23; }
-
-.reveal .controls div.navigate-left.enabled:hover {
- border-right-color: #dd5567; }
-
-.reveal .controls div.navigate-right.enabled:hover {
- border-left-color: #dd5567; }
-
-.reveal .controls div.navigate-up.enabled:hover {
- border-bottom-color: #dd5567; }
-
-.reveal .controls div.navigate-down.enabled:hover {
- border-top-color: #dd5567; }
-
-/*********************************************
- * PROGRESS BAR
- *********************************************/
-.reveal .progress {
- background: rgba(0, 0, 0, 0.2); }
-
-.reveal .progress span {
- background: #a23;
- -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
-
-/*********************************************
- * SLIDE NUMBER
- *********************************************/
-.reveal .slide-number {
- color: #a23; }
-
-.reveal p {
- font-weight: 300;
- text-shadow: 1px 1px #222; }
-
-.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {
- font-weight: 700; }
-
-.reveal a, .reveal a:hover {
- text-shadow: 2px 2px 2px #000; }
-
-.reveal small a, .reveal small a:hover {
- text-shadow: 1px 1px 1px #000; }
-
-.reveal p code {
- background-color: #23241f;
- display: inline-block;
- border-radius: 7px; }
-
-.reveal small code {
- vertical-align: baseline; }
diff --git a/presentation/css/theme/league.css b/presentation/css/theme/league.css
deleted file mode 100644
index b11d51d8d..000000000
--- a/presentation/css/theme/league.css
+++ /dev/null
@@ -1,267 +0,0 @@
-@import url(../../lib/font/league-gothic/league-gothic.css);
-@import url(../../contrib/google/css/lato.css);
-/**
- * League theme for reveal.js.
- *
- * This was the default theme pre-3.0.0.
- *
- * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
- */
-/*********************************************
- * GLOBAL STYLES
- *********************************************/
-body {
- background: #1c1e20;
- background: -moz-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%);
- background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, #555a5f), color-stop(100%, #1c1e20));
- background: -webkit-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%);
- background: -o-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%);
- background: -ms-radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%);
- background: radial-gradient(center, circle cover, #555a5f 0%, #1c1e20 100%);
- background-color: #2b2b2b; }
-
-.reveal {
- font-family: 'Lato', sans-serif;
- font-size: 36px;
- font-weight: normal;
- color: #eee; }
-
-::selection {
- color: #fff;
- background: #FF5E99;
- text-shadow: none; }
-
-.reveal .slides > section, .reveal .slides > section > section {
- line-height: 1.3;
- font-weight: inherit; }
-
-/*********************************************
- * HEADERS
- *********************************************/
-.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {
- margin: 0 0 20px 0;
- color: #eee;
- font-family: 'League Gothic', Impact, sans-serif;
- font-weight: normal;
- line-height: 1.2;
- letter-spacing: normal;
- text-transform: uppercase;
- text-shadow: 0px 0px 6px rgba(0, 0, 0, 0.2);
- word-wrap: break-word; }
-
-.reveal h1 {
- font-size: 3.77em; }
-
-.reveal h2 {
- font-size: 2.11em; }
-
-.reveal h3 {
- font-size: 1.55em; }
-
-.reveal h4 {
- font-size: 1em; }
-
-.reveal h1 {
- text-shadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0, 0, 0, 0.1), 0 0 5px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.3), 0 3px 5px rgba(0, 0, 0, 0.2), 0 5px 10px rgba(0, 0, 0, 0.25), 0 20px 20px rgba(0, 0, 0, 0.15); }
-
-/*********************************************
- * OTHER
- *********************************************/
-.reveal p {
- margin: 20px 0;
- line-height: 1.3; }
-
-/* Ensure certain elements are never larger than the slide itself */
-.reveal img, .reveal video, .reveal iframe {
- max-width: 95%;
- max-height: 95%; }
-
-.reveal strong, .reveal b {
- font-weight: bold; }
-
-.reveal em {
- font-style: italic; }
-
-.reveal ol, .reveal dl, .reveal ul {
- display: inline-block;
- text-align: left;
- margin: 0 0 0 1em; }
-
-.reveal ol {
- list-style-type: decimal; }
-
-.reveal ul {
- list-style-type: disc; }
-
-.reveal ul ul {
- list-style-type: square; }
-
-.reveal ul ul ul {
- list-style-type: circle; }
-
-.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul {
- display: block;
- margin-left: 40px; }
-
-.reveal dt {
- font-weight: bold; }
-
-.reveal dd {
- margin-left: 40px; }
-
-.reveal q, .reveal blockquote {
- quotes: none; }
-
-.reveal blockquote {
- display: block;
- position: relative;
- width: 70%;
- margin: 20px auto;
- padding: 5px;
- font-style: italic;
- background: rgba(255, 255, 255, 0.05);
- box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); }
-
-.reveal blockquote p:first-child, .reveal blockquote p:last-child {
- display: inline-block; }
-
-.reveal q {
- font-style: italic; }
-
-.reveal pre {
- display: block;
- position: relative;
- width: 90%;
- margin: 20px auto;
- text-align: left;
- font-size: 0.55em;
- font-family: monospace;
- line-height: 1.2em;
- word-wrap: break-word;
- box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
-
-.reveal code {
- font-family: monospace; }
-
-.reveal pre code {
- display: block;
- padding: 5px;
- overflow: auto;
- max-height: 400px;
- word-wrap: normal;
- background: #3F3F3F;
- color: #DCDCDC; }
-
-.reveal table {
- margin: auto;
- border-collapse: collapse;
- border-spacing: 0; }
-
-.reveal table th {
- font-weight: bold; }
-
-.reveal table th, .reveal table td {
- text-align: left;
- padding: 0.2em 0.5em 0.2em 0.5em;
- border-bottom: 1px solid; }
-
-.reveal table tr:last-child td {
- border-bottom: none; }
-
-.reveal sup {
- vertical-align: super; }
-
-.reveal sub {
- vertical-align: sub; }
-
-.reveal small {
- display: inline-block;
- font-size: 0.6em;
- line-height: 1.2em;
- vertical-align: top; }
-
-.reveal small * {
- vertical-align: top; }
-
-/*********************************************
- * LINKS
- *********************************************/
-.reveal a {
- color: #13DAEC;
- text-decoration: none;
- -webkit-transition: color 0.15s ease;
- -moz-transition: color 0.15s ease;
- transition: color 0.15s ease; }
-
-.reveal a:hover {
- color: #71ebf4;
- text-shadow: none;
- border: none; }
-
-.reveal .roll span:after {
- color: #fff;
- background: #0d9ba5; }
-
-/*********************************************
- * IMAGES
- *********************************************/
-.reveal section img {
- margin: 15px 0px;
- background: rgba(255, 255, 255, 0.12);
- border: 4px solid #eee;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); }
-
-.reveal a img {
- -webkit-transition: all 0.15s linear;
- -moz-transition: all 0.15s linear;
- transition: all 0.15s linear; }
-
-.reveal a:hover img {
- background: rgba(255, 255, 255, 0.2);
- border-color: #13DAEC;
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); }
-
-/*********************************************
- * NAVIGATION CONTROLS
- *********************************************/
-.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled {
- border-right-color: #13DAEC; }
-
-.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled {
- border-left-color: #13DAEC; }
-
-.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled {
- border-bottom-color: #13DAEC; }
-
-.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled {
- border-top-color: #13DAEC; }
-
-.reveal .controls div.navigate-left.enabled:hover {
- border-right-color: #71ebf4; }
-
-.reveal .controls div.navigate-right.enabled:hover {
- border-left-color: #71ebf4; }
-
-.reveal .controls div.navigate-up.enabled:hover {
- border-bottom-color: #71ebf4; }
-
-.reveal .controls div.navigate-down.enabled:hover {
- border-top-color: #71ebf4; }
-
-/*********************************************
- * PROGRESS BAR
- *********************************************/
-.reveal .progress {
- background: rgba(0, 0, 0, 0.2); }
-
-.reveal .progress span {
- background: #13DAEC;
- -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
-
-/*********************************************
- * SLIDE NUMBER
- *********************************************/
-.reveal .slide-number {
- color: #13DAEC; }
diff --git a/presentation/css/theme/moon.css b/presentation/css/theme/moon.css
deleted file mode 100644
index 4817c18de..000000000
--- a/presentation/css/theme/moon.css
+++ /dev/null
@@ -1,265 +0,0 @@
-@import url(../../lib/font/league-gothic/league-gothic.css);
-@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);
-/**
- * Solarized Dark theme for reveal.js.
- * Author: Achim Staebler
- */
-/**
- * Solarized colors by Ethan Schoonover
- */
-html * {
- color-profile: sRGB;
- rendering-intent: auto; }
-
-/*********************************************
- * GLOBAL STYLES
- *********************************************/
-body {
- background: #002b36;
- background-color: #002b36; }
-
-.reveal {
- font-family: 'Lato', sans-serif;
- font-size: 36px;
- font-weight: normal;
- color: #93a1a1; }
-
-::selection {
- color: #fff;
- background: #d33682;
- text-shadow: none; }
-
-.reveal .slides > section, .reveal .slides > section > section {
- line-height: 1.3;
- font-weight: inherit; }
-
-/*********************************************
- * HEADERS
- *********************************************/
-.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {
- margin: 0 0 20px 0;
- color: #eee8d5;
- font-family: 'League Gothic', Impact, sans-serif;
- font-weight: normal;
- line-height: 1.2;
- letter-spacing: normal;
- text-transform: uppercase;
- text-shadow: none;
- word-wrap: break-word; }
-
-.reveal h1 {
- font-size: 3.77em; }
-
-.reveal h2 {
- font-size: 2.11em; }
-
-.reveal h3 {
- font-size: 1.55em; }
-
-.reveal h4 {
- font-size: 1em; }
-
-.reveal h1 {
- text-shadow: none; }
-
-/*********************************************
- * OTHER
- *********************************************/
-.reveal p {
- margin: 20px 0;
- line-height: 1.3; }
-
-/* Ensure certain elements are never larger than the slide itself */
-.reveal img, .reveal video, .reveal iframe {
- max-width: 95%;
- max-height: 95%; }
-
-.reveal strong, .reveal b {
- font-weight: bold; }
-
-.reveal em {
- font-style: italic; }
-
-.reveal ol, .reveal dl, .reveal ul {
- display: inline-block;
- text-align: left;
- margin: 0 0 0 1em; }
-
-.reveal ol {
- list-style-type: decimal; }
-
-.reveal ul {
- list-style-type: disc; }
-
-.reveal ul ul {
- list-style-type: square; }
-
-.reveal ul ul ul {
- list-style-type: circle; }
-
-.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul {
- display: block;
- margin-left: 40px; }
-
-.reveal dt {
- font-weight: bold; }
-
-.reveal dd {
- margin-left: 40px; }
-
-.reveal q, .reveal blockquote {
- quotes: none; }
-
-.reveal blockquote {
- display: block;
- position: relative;
- width: 70%;
- margin: 20px auto;
- padding: 5px;
- font-style: italic;
- background: rgba(255, 255, 255, 0.05);
- box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); }
-
-.reveal blockquote p:first-child, .reveal blockquote p:last-child {
- display: inline-block; }
-
-.reveal q {
- font-style: italic; }
-
-.reveal pre {
- display: block;
- position: relative;
- width: 90%;
- margin: 20px auto;
- text-align: left;
- font-size: 0.55em;
- font-family: monospace;
- line-height: 1.2em;
- word-wrap: break-word;
- box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
-
-.reveal code {
- font-family: monospace; }
-
-.reveal pre code {
- display: block;
- padding: 5px;
- overflow: auto;
- max-height: 400px;
- word-wrap: normal;
- background: #3F3F3F;
- color: #DCDCDC; }
-
-.reveal table {
- margin: auto;
- border-collapse: collapse;
- border-spacing: 0; }
-
-.reveal table th {
- font-weight: bold; }
-
-.reveal table th, .reveal table td {
- text-align: left;
- padding: 0.2em 0.5em 0.2em 0.5em;
- border-bottom: 1px solid; }
-
-.reveal table tr:last-child td {
- border-bottom: none; }
-
-.reveal sup {
- vertical-align: super; }
-
-.reveal sub {
- vertical-align: sub; }
-
-.reveal small {
- display: inline-block;
- font-size: 0.6em;
- line-height: 1.2em;
- vertical-align: top; }
-
-.reveal small * {
- vertical-align: top; }
-
-/*********************************************
- * LINKS
- *********************************************/
-.reveal a {
- color: #268bd2;
- text-decoration: none;
- -webkit-transition: color 0.15s ease;
- -moz-transition: color 0.15s ease;
- transition: color 0.15s ease; }
-
-.reveal a:hover {
- color: #78bae6;
- text-shadow: none;
- border: none; }
-
-.reveal .roll span:after {
- color: #fff;
- background: #1a6291; }
-
-/*********************************************
- * IMAGES
- *********************************************/
-.reveal section img {
- margin: 15px 0px;
- background: rgba(255, 255, 255, 0.12);
- border: 4px solid #93a1a1;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); }
-
-.reveal a img {
- -webkit-transition: all 0.15s linear;
- -moz-transition: all 0.15s linear;
- transition: all 0.15s linear; }
-
-.reveal a:hover img {
- background: rgba(255, 255, 255, 0.2);
- border-color: #268bd2;
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); }
-
-/*********************************************
- * NAVIGATION CONTROLS
- *********************************************/
-.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled {
- border-right-color: #268bd2; }
-
-.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled {
- border-left-color: #268bd2; }
-
-.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled {
- border-bottom-color: #268bd2; }
-
-.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled {
- border-top-color: #268bd2; }
-
-.reveal .controls div.navigate-left.enabled:hover {
- border-right-color: #78bae6; }
-
-.reveal .controls div.navigate-right.enabled:hover {
- border-left-color: #78bae6; }
-
-.reveal .controls div.navigate-up.enabled:hover {
- border-bottom-color: #78bae6; }
-
-.reveal .controls div.navigate-down.enabled:hover {
- border-top-color: #78bae6; }
-
-/*********************************************
- * PROGRESS BAR
- *********************************************/
-.reveal .progress {
- background: rgba(0, 0, 0, 0.2); }
-
-.reveal .progress span {
- background: #268bd2;
- -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
-
-/*********************************************
- * SLIDE NUMBER
- *********************************************/
-.reveal .slide-number {
- color: #268bd2; }
diff --git a/presentation/css/theme/night.css b/presentation/css/theme/night.css
deleted file mode 100644
index 71319b2b9..000000000
--- a/presentation/css/theme/night.css
+++ /dev/null
@@ -1,259 +0,0 @@
-@import url(https://fonts.googleapis.com/css?family=Montserrat:700);
-@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,700,400italic,700italic);
-/**
- * Black theme for reveal.js.
- *
- * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
- */
-/*********************************************
- * GLOBAL STYLES
- *********************************************/
-body {
- background: #111;
- background-color: #111; }
-
-.reveal {
- font-family: 'Open Sans', sans-serif;
- font-size: 30px;
- font-weight: normal;
- color: #eee; }
-
-::selection {
- color: #fff;
- background: #e7ad52;
- text-shadow: none; }
-
-.reveal .slides > section, .reveal .slides > section > section {
- line-height: 1.3;
- font-weight: inherit; }
-
-/*********************************************
- * HEADERS
- *********************************************/
-.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {
- margin: 0 0 20px 0;
- color: #eee;
- font-family: 'Montserrat', Impact, sans-serif;
- font-weight: normal;
- line-height: 1.2;
- letter-spacing: -0.03em;
- text-transform: none;
- text-shadow: none;
- word-wrap: break-word; }
-
-.reveal h1 {
- font-size: 3.77em; }
-
-.reveal h2 {
- font-size: 2.11em; }
-
-.reveal h3 {
- font-size: 1.55em; }
-
-.reveal h4 {
- font-size: 1em; }
-
-.reveal h1 {
- text-shadow: none; }
-
-/*********************************************
- * OTHER
- *********************************************/
-.reveal p {
- margin: 20px 0;
- line-height: 1.3; }
-
-/* Ensure certain elements are never larger than the slide itself */
-.reveal img, .reveal video, .reveal iframe {
- max-width: 95%;
- max-height: 95%; }
-
-.reveal strong, .reveal b {
- font-weight: bold; }
-
-.reveal em {
- font-style: italic; }
-
-.reveal ol, .reveal dl, .reveal ul {
- display: inline-block;
- text-align: left;
- margin: 0 0 0 1em; }
-
-.reveal ol {
- list-style-type: decimal; }
-
-.reveal ul {
- list-style-type: disc; }
-
-.reveal ul ul {
- list-style-type: square; }
-
-.reveal ul ul ul {
- list-style-type: circle; }
-
-.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul {
- display: block;
- margin-left: 40px; }
-
-.reveal dt {
- font-weight: bold; }
-
-.reveal dd {
- margin-left: 40px; }
-
-.reveal q, .reveal blockquote {
- quotes: none; }
-
-.reveal blockquote {
- display: block;
- position: relative;
- width: 70%;
- margin: 20px auto;
- padding: 5px;
- font-style: italic;
- background: rgba(255, 255, 255, 0.05);
- box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); }
-
-.reveal blockquote p:first-child, .reveal blockquote p:last-child {
- display: inline-block; }
-
-.reveal q {
- font-style: italic; }
-
-.reveal pre {
- display: block;
- position: relative;
- width: 90%;
- margin: 20px auto;
- text-align: left;
- font-size: 0.55em;
- font-family: monospace;
- line-height: 1.2em;
- word-wrap: break-word;
- box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
-
-.reveal code {
- font-family: monospace; }
-
-.reveal pre code {
- display: block;
- padding: 5px;
- overflow: auto;
- max-height: 400px;
- word-wrap: normal;
- background: #3F3F3F;
- color: #DCDCDC; }
-
-.reveal table {
- margin: auto;
- border-collapse: collapse;
- border-spacing: 0; }
-
-.reveal table th {
- font-weight: bold; }
-
-.reveal table th, .reveal table td {
- text-align: left;
- padding: 0.2em 0.5em 0.2em 0.5em;
- border-bottom: 1px solid; }
-
-.reveal table tr:last-child td {
- border-bottom: none; }
-
-.reveal sup {
- vertical-align: super; }
-
-.reveal sub {
- vertical-align: sub; }
-
-.reveal small {
- display: inline-block;
- font-size: 0.6em;
- line-height: 1.2em;
- vertical-align: top; }
-
-.reveal small * {
- vertical-align: top; }
-
-/*********************************************
- * LINKS
- *********************************************/
-.reveal a {
- color: #e7ad52;
- text-decoration: none;
- -webkit-transition: color 0.15s ease;
- -moz-transition: color 0.15s ease;
- transition: color 0.15s ease; }
-
-.reveal a:hover {
- color: #f3d7ac;
- text-shadow: none;
- border: none; }
-
-.reveal .roll span:after {
- color: #fff;
- background: #d0881d; }
-
-/*********************************************
- * IMAGES
- *********************************************/
-.reveal section img {
- margin: 15px 0px;
- background: rgba(255, 255, 255, 0.12);
- border: 4px solid #eee;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); }
-
-.reveal a img {
- -webkit-transition: all 0.15s linear;
- -moz-transition: all 0.15s linear;
- transition: all 0.15s linear; }
-
-.reveal a:hover img {
- background: rgba(255, 255, 255, 0.2);
- border-color: #e7ad52;
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); }
-
-/*********************************************
- * NAVIGATION CONTROLS
- *********************************************/
-.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled {
- border-right-color: #e7ad52; }
-
-.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled {
- border-left-color: #e7ad52; }
-
-.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled {
- border-bottom-color: #e7ad52; }
-
-.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled {
- border-top-color: #e7ad52; }
-
-.reveal .controls div.navigate-left.enabled:hover {
- border-right-color: #f3d7ac; }
-
-.reveal .controls div.navigate-right.enabled:hover {
- border-left-color: #f3d7ac; }
-
-.reveal .controls div.navigate-up.enabled:hover {
- border-bottom-color: #f3d7ac; }
-
-.reveal .controls div.navigate-down.enabled:hover {
- border-top-color: #f3d7ac; }
-
-/*********************************************
- * PROGRESS BAR
- *********************************************/
-.reveal .progress {
- background: rgba(0, 0, 0, 0.2); }
-
-.reveal .progress span {
- background: #e7ad52;
- -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
-
-/*********************************************
- * SLIDE NUMBER
- *********************************************/
-.reveal .slide-number {
- color: #e7ad52; }
diff --git a/presentation/css/theme/serif.css b/presentation/css/theme/serif.css
deleted file mode 100644
index ed85db681..000000000
--- a/presentation/css/theme/serif.css
+++ /dev/null
@@ -1,261 +0,0 @@
-/**
- * A simple theme for reveal.js presentations, similar
- * to the default theme. The accent color is brown.
- *
- * This theme is Copyright (C) 2012-2013 Owen Versteeg, http://owenversteeg.com - it is MIT licensed.
- */
-.reveal a {
- line-height: 1.3em; }
-
-/*********************************************
- * GLOBAL STYLES
- *********************************************/
-body {
- background: #F0F1EB;
- background-color: #F0F1EB; }
-
-.reveal {
- font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
- font-size: 36px;
- font-weight: normal;
- color: #000; }
-
-::selection {
- color: #fff;
- background: #26351C;
- text-shadow: none; }
-
-.reveal .slides > section, .reveal .slides > section > section {
- line-height: 1.3;
- font-weight: inherit; }
-
-/*********************************************
- * HEADERS
- *********************************************/
-.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {
- margin: 0 0 20px 0;
- color: #383D3D;
- font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
- font-weight: normal;
- line-height: 1.2;
- letter-spacing: normal;
- text-transform: none;
- text-shadow: none;
- word-wrap: break-word; }
-
-.reveal h1 {
- font-size: 3.77em; }
-
-.reveal h2 {
- font-size: 2.11em; }
-
-.reveal h3 {
- font-size: 1.55em; }
-
-.reveal h4 {
- font-size: 1em; }
-
-.reveal h1 {
- text-shadow: none; }
-
-/*********************************************
- * OTHER
- *********************************************/
-.reveal p {
- margin: 20px 0;
- line-height: 1.3; }
-
-/* Ensure certain elements are never larger than the slide itself */
-.reveal img, .reveal video, .reveal iframe {
- max-width: 95%;
- max-height: 95%; }
-
-.reveal strong, .reveal b {
- font-weight: bold; }
-
-.reveal em {
- font-style: italic; }
-
-.reveal ol, .reveal dl, .reveal ul {
- display: inline-block;
- text-align: left;
- margin: 0 0 0 1em; }
-
-.reveal ol {
- list-style-type: decimal; }
-
-.reveal ul {
- list-style-type: disc; }
-
-.reveal ul ul {
- list-style-type: square; }
-
-.reveal ul ul ul {
- list-style-type: circle; }
-
-.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul {
- display: block;
- margin-left: 40px; }
-
-.reveal dt {
- font-weight: bold; }
-
-.reveal dd {
- margin-left: 40px; }
-
-.reveal q, .reveal blockquote {
- quotes: none; }
-
-.reveal blockquote {
- display: block;
- position: relative;
- width: 70%;
- margin: 20px auto;
- padding: 5px;
- font-style: italic;
- background: rgba(255, 255, 255, 0.05);
- box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); }
-
-.reveal blockquote p:first-child, .reveal blockquote p:last-child {
- display: inline-block; }
-
-.reveal q {
- font-style: italic; }
-
-.reveal pre {
- display: block;
- position: relative;
- width: 90%;
- margin: 20px auto;
- text-align: left;
- font-size: 0.55em;
- font-family: monospace;
- line-height: 1.2em;
- word-wrap: break-word;
- box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
-
-.reveal code {
- font-family: monospace; }
-
-.reveal pre code {
- display: block;
- padding: 5px;
- overflow: auto;
- max-height: 400px;
- word-wrap: normal;
- background: #3F3F3F;
- color: #DCDCDC; }
-
-.reveal table {
- margin: auto;
- border-collapse: collapse;
- border-spacing: 0; }
-
-.reveal table th {
- font-weight: bold; }
-
-.reveal table th, .reveal table td {
- text-align: left;
- padding: 0.2em 0.5em 0.2em 0.5em;
- border-bottom: 1px solid; }
-
-.reveal table tr:last-child td {
- border-bottom: none; }
-
-.reveal sup {
- vertical-align: super; }
-
-.reveal sub {
- vertical-align: sub; }
-
-.reveal small {
- display: inline-block;
- font-size: 0.6em;
- line-height: 1.2em;
- vertical-align: top; }
-
-.reveal small * {
- vertical-align: top; }
-
-/*********************************************
- * LINKS
- *********************************************/
-.reveal a {
- color: #51483D;
- text-decoration: none;
- -webkit-transition: color 0.15s ease;
- -moz-transition: color 0.15s ease;
- transition: color 0.15s ease; }
-
-.reveal a:hover {
- color: #8b7b69;
- text-shadow: none;
- border: none; }
-
-.reveal .roll span:after {
- color: #fff;
- background: #25211c; }
-
-/*********************************************
- * IMAGES
- *********************************************/
-.reveal section img {
- margin: 15px 0px;
- background: rgba(255, 255, 255, 0.12);
- border: 4px solid #000;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); }
-
-.reveal a img {
- -webkit-transition: all 0.15s linear;
- -moz-transition: all 0.15s linear;
- transition: all 0.15s linear; }
-
-.reveal a:hover img {
- background: rgba(255, 255, 255, 0.2);
- border-color: #51483D;
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); }
-
-/*********************************************
- * NAVIGATION CONTROLS
- *********************************************/
-.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled {
- border-right-color: #51483D; }
-
-.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled {
- border-left-color: #51483D; }
-
-.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled {
- border-bottom-color: #51483D; }
-
-.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled {
- border-top-color: #51483D; }
-
-.reveal .controls div.navigate-left.enabled:hover {
- border-right-color: #8b7b69; }
-
-.reveal .controls div.navigate-right.enabled:hover {
- border-left-color: #8b7b69; }
-
-.reveal .controls div.navigate-up.enabled:hover {
- border-bottom-color: #8b7b69; }
-
-.reveal .controls div.navigate-down.enabled:hover {
- border-top-color: #8b7b69; }
-
-/*********************************************
- * PROGRESS BAR
- *********************************************/
-.reveal .progress {
- background: rgba(0, 0, 0, 0.2); }
-
-.reveal .progress span {
- background: #51483D;
- -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
-
-/*********************************************
- * SLIDE NUMBER
- *********************************************/
-.reveal .slide-number {
- color: #51483D; }
diff --git a/presentation/css/theme/simple.css b/presentation/css/theme/simple.css
deleted file mode 100644
index d9153d7e9..000000000
--- a/presentation/css/theme/simple.css
+++ /dev/null
@@ -1,261 +0,0 @@
-@import url(https://fonts.googleapis.com/css?family=News+Cycle:400,700);
-@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);
-/**
- * A simple theme for reveal.js presentations, similar
- * to the default theme. The accent color is darkblue.
- *
- * This theme is Copyright (C) 2012 Owen Versteeg, https://github.com/StereotypicalApps. It is MIT licensed.
- * reveal.js is Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
- */
-/*********************************************
- * GLOBAL STYLES
- *********************************************/
-body {
- background: #fff;
- background-color: #fff; }
-
-.reveal {
- font-family: 'Lato', sans-serif;
- font-size: 36px;
- font-weight: normal;
- color: #000; }
-
-::selection {
- color: #fff;
- background: rgba(0, 0, 0, 0.99);
- text-shadow: none; }
-
-.reveal .slides > section, .reveal .slides > section > section {
- line-height: 1.3;
- font-weight: inherit; }
-
-/*********************************************
- * HEADERS
- *********************************************/
-.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {
- margin: 0 0 20px 0;
- color: #000;
- font-family: 'News Cycle', Impact, sans-serif;
- font-weight: normal;
- line-height: 1.2;
- letter-spacing: normal;
- text-transform: none;
- text-shadow: none;
- word-wrap: break-word; }
-
-.reveal h1 {
- font-size: 3.77em; }
-
-.reveal h2 {
- font-size: 2.11em; }
-
-.reveal h3 {
- font-size: 1.55em; }
-
-.reveal h4 {
- font-size: 1em; }
-
-.reveal h1 {
- text-shadow: none; }
-
-/*********************************************
- * OTHER
- *********************************************/
-.reveal p {
- margin: 20px 0;
- line-height: 1.3; }
-
-/* Ensure certain elements are never larger than the slide itself */
-.reveal img, .reveal video, .reveal iframe {
- max-width: 95%;
- max-height: 95%; }
-
-.reveal strong, .reveal b {
- font-weight: bold; }
-
-.reveal em {
- font-style: italic; }
-
-.reveal ol, .reveal dl, .reveal ul {
- display: inline-block;
- text-align: left;
- margin: 0 0 0 1em; }
-
-.reveal ol {
- list-style-type: decimal; }
-
-.reveal ul {
- list-style-type: disc; }
-
-.reveal ul ul {
- list-style-type: square; }
-
-.reveal ul ul ul {
- list-style-type: circle; }
-
-.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul {
- display: block;
- margin-left: 40px; }
-
-.reveal dt {
- font-weight: bold; }
-
-.reveal dd {
- margin-left: 40px; }
-
-.reveal q, .reveal blockquote {
- quotes: none; }
-
-.reveal blockquote {
- display: block;
- position: relative;
- width: 70%;
- margin: 20px auto;
- padding: 5px;
- font-style: italic;
- background: rgba(255, 255, 255, 0.05);
- box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); }
-
-.reveal blockquote p:first-child, .reveal blockquote p:last-child {
- display: inline-block; }
-
-.reveal q {
- font-style: italic; }
-
-.reveal pre {
- display: block;
- position: relative;
- width: 90%;
- margin: 20px auto;
- text-align: left;
- font-size: 0.55em;
- font-family: monospace;
- line-height: 1.2em;
- word-wrap: break-word;
- box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
-
-.reveal code {
- font-family: monospace; }
-
-.reveal pre code {
- display: block;
- padding: 5px;
- overflow: auto;
- max-height: 400px;
- word-wrap: normal;
- background: #3F3F3F;
- color: #DCDCDC; }
-
-.reveal table {
- margin: auto;
- border-collapse: collapse;
- border-spacing: 0; }
-
-.reveal table th {
- font-weight: bold; }
-
-.reveal table th, .reveal table td {
- text-align: left;
- padding: 0.2em 0.5em 0.2em 0.5em;
- border-bottom: 1px solid; }
-
-.reveal table tr:last-child td {
- border-bottom: none; }
-
-.reveal sup {
- vertical-align: super; }
-
-.reveal sub {
- vertical-align: sub; }
-
-.reveal small {
- display: inline-block;
- font-size: 0.6em;
- line-height: 1.2em;
- vertical-align: top; }
-
-.reveal small * {
- vertical-align: top; }
-
-/*********************************************
- * LINKS
- *********************************************/
-.reveal a {
- color: #00008B;
- text-decoration: none;
- -webkit-transition: color 0.15s ease;
- -moz-transition: color 0.15s ease;
- transition: color 0.15s ease; }
-
-.reveal a:hover {
- color: #0000f1;
- text-shadow: none;
- border: none; }
-
-.reveal .roll span:after {
- color: #fff;
- background: #00003f; }
-
-/*********************************************
- * IMAGES
- *********************************************/
-.reveal section img {
- margin: 15px 0px;
- background: rgba(255, 255, 255, 0.12);
- border: 4px solid #000;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); }
-
-.reveal a img {
- -webkit-transition: all 0.15s linear;
- -moz-transition: all 0.15s linear;
- transition: all 0.15s linear; }
-
-.reveal a:hover img {
- background: rgba(255, 255, 255, 0.2);
- border-color: #00008B;
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); }
-
-/*********************************************
- * NAVIGATION CONTROLS
- *********************************************/
-.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled {
- border-right-color: #00008B; }
-
-.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled {
- border-left-color: #00008B; }
-
-.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled {
- border-bottom-color: #00008B; }
-
-.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled {
- border-top-color: #00008B; }
-
-.reveal .controls div.navigate-left.enabled:hover {
- border-right-color: #0000f1; }
-
-.reveal .controls div.navigate-right.enabled:hover {
- border-left-color: #0000f1; }
-
-.reveal .controls div.navigate-up.enabled:hover {
- border-bottom-color: #0000f1; }
-
-.reveal .controls div.navigate-down.enabled:hover {
- border-top-color: #0000f1; }
-
-/*********************************************
- * PROGRESS BAR
- *********************************************/
-.reveal .progress {
- background: rgba(0, 0, 0, 0.2); }
-
-.reveal .progress span {
- background: #00008B;
- -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
-
-/*********************************************
- * SLIDE NUMBER
- *********************************************/
-.reveal .slide-number {
- color: #00008B; }
diff --git a/presentation/css/theme/sky.css b/presentation/css/theme/sky.css
deleted file mode 100644
index f7e840225..000000000
--- a/presentation/css/theme/sky.css
+++ /dev/null
@@ -1,268 +0,0 @@
-@import url(https://fonts.googleapis.com/css?family=Quicksand:400,700,400italic,700italic);
-@import url(https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700);
-/**
- * Sky theme for reveal.js.
- *
- * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
- */
-.reveal a {
- line-height: 1.3em; }
-
-/*********************************************
- * GLOBAL STYLES
- *********************************************/
-body {
- background: #add9e4;
- background: -moz-radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%);
- background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%, #f7fbfc), color-stop(100%, #add9e4));
- background: -webkit-radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%);
- background: -o-radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%);
- background: -ms-radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%);
- background: radial-gradient(center, circle cover, #f7fbfc 0%, #add9e4 100%);
- background-color: #f7fbfc; }
-
-.reveal {
- font-family: 'Open Sans', sans-serif;
- font-size: 36px;
- font-weight: normal;
- color: #333; }
-
-::selection {
- color: #fff;
- background: #134674;
- text-shadow: none; }
-
-.reveal .slides > section, .reveal .slides > section > section {
- line-height: 1.3;
- font-weight: inherit; }
-
-/*********************************************
- * HEADERS
- *********************************************/
-.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {
- margin: 0 0 20px 0;
- color: #333;
- font-family: 'Quicksand', sans-serif;
- font-weight: normal;
- line-height: 1.2;
- letter-spacing: -0.08em;
- text-transform: uppercase;
- text-shadow: none;
- word-wrap: break-word; }
-
-.reveal h1 {
- font-size: 3.77em; }
-
-.reveal h2 {
- font-size: 2.11em; }
-
-.reveal h3 {
- font-size: 1.55em; }
-
-.reveal h4 {
- font-size: 1em; }
-
-.reveal h1 {
- text-shadow: none; }
-
-/*********************************************
- * OTHER
- *********************************************/
-.reveal p {
- margin: 20px 0;
- line-height: 1.3; }
-
-/* Ensure certain elements are never larger than the slide itself */
-.reveal img, .reveal video, .reveal iframe {
- max-width: 95%;
- max-height: 95%; }
-
-.reveal strong, .reveal b {
- font-weight: bold; }
-
-.reveal em {
- font-style: italic; }
-
-.reveal ol, .reveal dl, .reveal ul {
- display: inline-block;
- text-align: left;
- margin: 0 0 0 1em; }
-
-.reveal ol {
- list-style-type: decimal; }
-
-.reveal ul {
- list-style-type: disc; }
-
-.reveal ul ul {
- list-style-type: square; }
-
-.reveal ul ul ul {
- list-style-type: circle; }
-
-.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul {
- display: block;
- margin-left: 40px; }
-
-.reveal dt {
- font-weight: bold; }
-
-.reveal dd {
- margin-left: 40px; }
-
-.reveal q, .reveal blockquote {
- quotes: none; }
-
-.reveal blockquote {
- display: block;
- position: relative;
- width: 70%;
- margin: 20px auto;
- padding: 5px;
- font-style: italic;
- background: rgba(255, 255, 255, 0.05);
- box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); }
-
-.reveal blockquote p:first-child, .reveal blockquote p:last-child {
- display: inline-block; }
-
-.reveal q {
- font-style: italic; }
-
-.reveal pre {
- display: block;
- position: relative;
- width: 90%;
- margin: 20px auto;
- text-align: left;
- font-size: 0.55em;
- font-family: monospace;
- line-height: 1.2em;
- word-wrap: break-word;
- box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
-
-.reveal code {
- font-family: monospace; }
-
-.reveal pre code {
- display: block;
- padding: 5px;
- overflow: auto;
- max-height: 400px;
- word-wrap: normal;
- background: #3F3F3F;
- color: #DCDCDC; }
-
-.reveal table {
- margin: auto;
- border-collapse: collapse;
- border-spacing: 0; }
-
-.reveal table th {
- font-weight: bold; }
-
-.reveal table th, .reveal table td {
- text-align: left;
- padding: 0.2em 0.5em 0.2em 0.5em;
- border-bottom: 1px solid; }
-
-.reveal table tr:last-child td {
- border-bottom: none; }
-
-.reveal sup {
- vertical-align: super; }
-
-.reveal sub {
- vertical-align: sub; }
-
-.reveal small {
- display: inline-block;
- font-size: 0.6em;
- line-height: 1.2em;
- vertical-align: top; }
-
-.reveal small * {
- vertical-align: top; }
-
-/*********************************************
- * LINKS
- *********************************************/
-.reveal a {
- color: #3b759e;
- text-decoration: none;
- -webkit-transition: color 0.15s ease;
- -moz-transition: color 0.15s ease;
- transition: color 0.15s ease; }
-
-.reveal a:hover {
- color: #74a8cb;
- text-shadow: none;
- border: none; }
-
-.reveal .roll span:after {
- color: #fff;
- background: #264d66; }
-
-/*********************************************
- * IMAGES
- *********************************************/
-.reveal section img {
- margin: 15px 0px;
- background: rgba(255, 255, 255, 0.12);
- border: 4px solid #333;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); }
-
-.reveal a img {
- -webkit-transition: all 0.15s linear;
- -moz-transition: all 0.15s linear;
- transition: all 0.15s linear; }
-
-.reveal a:hover img {
- background: rgba(255, 255, 255, 0.2);
- border-color: #3b759e;
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); }
-
-/*********************************************
- * NAVIGATION CONTROLS
- *********************************************/
-.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled {
- border-right-color: #3b759e; }
-
-.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled {
- border-left-color: #3b759e; }
-
-.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled {
- border-bottom-color: #3b759e; }
-
-.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled {
- border-top-color: #3b759e; }
-
-.reveal .controls div.navigate-left.enabled:hover {
- border-right-color: #74a8cb; }
-
-.reveal .controls div.navigate-right.enabled:hover {
- border-left-color: #74a8cb; }
-
-.reveal .controls div.navigate-up.enabled:hover {
- border-bottom-color: #74a8cb; }
-
-.reveal .controls div.navigate-down.enabled:hover {
- border-top-color: #74a8cb; }
-
-/*********************************************
- * PROGRESS BAR
- *********************************************/
-.reveal .progress {
- background: rgba(0, 0, 0, 0.2); }
-
-.reveal .progress span {
- background: #3b759e;
- -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
-
-/*********************************************
- * SLIDE NUMBER
- *********************************************/
-.reveal .slide-number {
- color: #3b759e; }
diff --git a/presentation/css/theme/solarized.css b/presentation/css/theme/solarized.css
deleted file mode 100644
index 583201a36..000000000
--- a/presentation/css/theme/solarized.css
+++ /dev/null
@@ -1,265 +0,0 @@
-@import url(../../lib/font/league-gothic/league-gothic.css);
-@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);
-/**
- * Solarized Light theme for reveal.js.
- * Author: Achim Staebler
- */
-/**
- * Solarized colors by Ethan Schoonover
- */
-html * {
- color-profile: sRGB;
- rendering-intent: auto; }
-
-/*********************************************
- * GLOBAL STYLES
- *********************************************/
-body {
- background: #fdf6e3;
- background-color: #fdf6e3; }
-
-.reveal {
- font-family: 'Lato', sans-serif;
- font-size: 36px;
- font-weight: normal;
- color: #657b83; }
-
-::selection {
- color: #fff;
- background: #d33682;
- text-shadow: none; }
-
-.reveal .slides > section, .reveal .slides > section > section {
- line-height: 1.3;
- font-weight: inherit; }
-
-/*********************************************
- * HEADERS
- *********************************************/
-.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {
- margin: 0 0 20px 0;
- color: #586e75;
- font-family: 'League Gothic', Impact, sans-serif;
- font-weight: normal;
- line-height: 1.2;
- letter-spacing: normal;
- text-transform: uppercase;
- text-shadow: none;
- word-wrap: break-word; }
-
-.reveal h1 {
- font-size: 3.77em; }
-
-.reveal h2 {
- font-size: 2.11em; }
-
-.reveal h3 {
- font-size: 1.55em; }
-
-.reveal h4 {
- font-size: 1em; }
-
-.reveal h1 {
- text-shadow: none; }
-
-/*********************************************
- * OTHER
- *********************************************/
-.reveal p {
- margin: 20px 0;
- line-height: 1.3; }
-
-/* Ensure certain elements are never larger than the slide itself */
-.reveal img, .reveal video, .reveal iframe {
- max-width: 95%;
- max-height: 95%; }
-
-.reveal strong, .reveal b {
- font-weight: bold; }
-
-.reveal em {
- font-style: italic; }
-
-.reveal ol, .reveal dl, .reveal ul {
- display: inline-block;
- text-align: left;
- margin: 0 0 0 1em; }
-
-.reveal ol {
- list-style-type: decimal; }
-
-.reveal ul {
- list-style-type: disc; }
-
-.reveal ul ul {
- list-style-type: square; }
-
-.reveal ul ul ul {
- list-style-type: circle; }
-
-.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul {
- display: block;
- margin-left: 40px; }
-
-.reveal dt {
- font-weight: bold; }
-
-.reveal dd {
- margin-left: 40px; }
-
-.reveal q, .reveal blockquote {
- quotes: none; }
-
-.reveal blockquote {
- display: block;
- position: relative;
- width: 70%;
- margin: 20px auto;
- padding: 5px;
- font-style: italic;
- background: rgba(255, 255, 255, 0.05);
- box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); }
-
-.reveal blockquote p:first-child, .reveal blockquote p:last-child {
- display: inline-block; }
-
-.reveal q {
- font-style: italic; }
-
-.reveal pre {
- display: block;
- position: relative;
- width: 90%;
- margin: 20px auto;
- text-align: left;
- font-size: 0.55em;
- font-family: monospace;
- line-height: 1.2em;
- word-wrap: break-word;
- box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
-
-.reveal code {
- font-family: monospace; }
-
-.reveal pre code {
- display: block;
- padding: 5px;
- overflow: auto;
- max-height: 400px;
- word-wrap: normal;
- background: #3F3F3F;
- color: #DCDCDC; }
-
-.reveal table {
- margin: auto;
- border-collapse: collapse;
- border-spacing: 0; }
-
-.reveal table th {
- font-weight: bold; }
-
-.reveal table th, .reveal table td {
- text-align: left;
- padding: 0.2em 0.5em 0.2em 0.5em;
- border-bottom: 1px solid; }
-
-.reveal table tr:last-child td {
- border-bottom: none; }
-
-.reveal sup {
- vertical-align: super; }
-
-.reveal sub {
- vertical-align: sub; }
-
-.reveal small {
- display: inline-block;
- font-size: 0.6em;
- line-height: 1.2em;
- vertical-align: top; }
-
-.reveal small * {
- vertical-align: top; }
-
-/*********************************************
- * LINKS
- *********************************************/
-.reveal a {
- color: #268bd2;
- text-decoration: none;
- -webkit-transition: color 0.15s ease;
- -moz-transition: color 0.15s ease;
- transition: color 0.15s ease; }
-
-.reveal a:hover {
- color: #78bae6;
- text-shadow: none;
- border: none; }
-
-.reveal .roll span:after {
- color: #fff;
- background: #1a6291; }
-
-/*********************************************
- * IMAGES
- *********************************************/
-.reveal section img {
- margin: 15px 0px;
- background: rgba(255, 255, 255, 0.12);
- border: 4px solid #657b83;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); }
-
-.reveal a img {
- -webkit-transition: all 0.15s linear;
- -moz-transition: all 0.15s linear;
- transition: all 0.15s linear; }
-
-.reveal a:hover img {
- background: rgba(255, 255, 255, 0.2);
- border-color: #268bd2;
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); }
-
-/*********************************************
- * NAVIGATION CONTROLS
- *********************************************/
-.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled {
- border-right-color: #268bd2; }
-
-.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled {
- border-left-color: #268bd2; }
-
-.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled {
- border-bottom-color: #268bd2; }
-
-.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled {
- border-top-color: #268bd2; }
-
-.reveal .controls div.navigate-left.enabled:hover {
- border-right-color: #78bae6; }
-
-.reveal .controls div.navigate-right.enabled:hover {
- border-left-color: #78bae6; }
-
-.reveal .controls div.navigate-up.enabled:hover {
- border-bottom-color: #78bae6; }
-
-.reveal .controls div.navigate-down.enabled:hover {
- border-top-color: #78bae6; }
-
-/*********************************************
- * PROGRESS BAR
- *********************************************/
-.reveal .progress {
- background: rgba(0, 0, 0, 0.2); }
-
-.reveal .progress span {
- background: #268bd2;
- -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
-
-/*********************************************
- * SLIDE NUMBER
- *********************************************/
-.reveal .slide-number {
- color: #268bd2; }
diff --git a/presentation/css/theme/source/beige.scss b/presentation/css/theme/source/beige.scss
deleted file mode 100644
index 5564f5389..000000000
--- a/presentation/css/theme/source/beige.scss
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Beige theme for reveal.js.
- *
- * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
- */
-
-
-// Default mixins and settings -----------------
-@import "../template/mixins";
-@import "../template/settings";
-// ---------------------------------------------
-
-
-
-// Include theme-specific fonts
-@import url(../../lib/font/league-gothic/league-gothic.css);
-@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);
-
-
-// Override theme settings (see ../template/settings.scss)
-$mainColor: #333;
-$headingColor: #333;
-$headingTextShadow: none;
-$backgroundColor: #f7f3de;
-$linkColor: #8b743d;
-$linkColorHover: lighten( $linkColor, 20% );
-$selectionBackgroundColor: rgba(79, 64, 28, 0.99);
-$heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15);
-
-// Background generator
-@mixin bodyBackground() {
- @include radial-gradient( rgba(247,242,211,1), rgba(255,255,255,1) );
-}
-
-
-
-// Theme template ------------------------------
-@import "../template/theme";
-// ---------------------------------------------
\ No newline at end of file
diff --git a/presentation/css/theme/source/black.scss b/presentation/css/theme/source/black.scss
deleted file mode 100644
index 73dfecb0a..000000000
--- a/presentation/css/theme/source/black.scss
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * Black theme for reveal.js. This is the opposite of the 'white' theme.
- *
- * Copyright (C) 2015 Hakim El Hattab, http://hakim.se
- */
-
-
-// Default mixins and settings -----------------
-@import "../template/mixins";
-@import "../template/settings";
-// ---------------------------------------------
-
-
-// Include theme-specific fonts
-@import url(../../lib/font/source-sans-pro/source-sans-pro.css);
-
-
-// Override theme settings (see ../template/settings.scss)
-$backgroundColor: #222;
-
-$mainColor: #fff;
-$headingColor: #fff;
-
-$mainFontSize: 38px;
-$mainFont: 'Source Sans Pro', Helvetica, sans-serif;
-$headingFont: 'Source Sans Pro', Helvetica, sans-serif;
-$headingTextShadow: none;
-$headingLetterSpacing: normal;
-$headingTextTransform: uppercase;
-$headingFontWeight: 600;
-$linkColor: #42affa;
-$linkColorHover: lighten( $linkColor, 15% );
-$selectionBackgroundColor: lighten( $linkColor, 25% );
-
-$heading1Size: 2.5em;
-$heading2Size: 1.6em;
-$heading3Size: 1.3em;
-$heading4Size: 1.0em;
-
-section.has-light-background {
- &, h1, h2, h3, h4, h5, h6 {
- color: #222;
- }
-}
-
-
-// Theme template ------------------------------
-@import "../template/theme";
-// ---------------------------------------------
\ No newline at end of file
diff --git a/presentation/css/theme/source/blood.scss b/presentation/css/theme/source/blood.scss
deleted file mode 100644
index f2e3a09e6..000000000
--- a/presentation/css/theme/source/blood.scss
+++ /dev/null
@@ -1,91 +0,0 @@
-/**
- * Blood theme for reveal.js
- * Author: Walther http://github.com/Walther
- *
- * Designed to be used with highlight.js theme
- * "monokai_sublime.css" available from
- * https://github.com/isagalaev/highlight.js/
- *
- * For other themes, change $codeBackground accordingly.
- *
- */
-
- // Default mixins and settings -----------------
-@import "../template/mixins";
-@import "../template/settings";
-// ---------------------------------------------
-
-// Include theme-specific fonts
-
-@import url(https://fonts.googleapis.com/css?family=Ubuntu:300,700,300italic,700italic);
-
-// Colors used in the theme
-$blood: #a23;
-$coal: #222;
-$codeBackground: #23241f;
-
-// Main text
-$mainFont: Ubuntu, 'sans-serif';
-$mainFontSize: 36px;
-$mainColor: #eee;
-
-// Headings
-$headingFont: Ubuntu, 'sans-serif';
-$headingTextShadow: 2px 2px 2px $coal;
-
-// h1 shadow, borrowed humbly from
-// (c) Default theme by Hakim El Hattab
-$heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15);
-
-// Links
-$linkColor: $blood;
-$linkColorHover: lighten( $linkColor, 20% );
-
-// Text selection
-$selectionBackgroundColor: $blood;
-$selectionColor: #fff;
-
-// Background generator
-@mixin bodyBackground() {
- @include radial-gradient( $coal, lighten( $coal, 25% ) );
-}
-
-// Theme template ------------------------------
-@import "../template/theme";
-// ---------------------------------------------
-
-// some overrides after theme template import
-
-.reveal p {
- font-weight: 300;
- text-shadow: 1px 1px $coal;
-}
-
-.reveal h1,
-.reveal h2,
-.reveal h3,
-.reveal h4,
-.reveal h5,
-.reveal h6 {
- font-weight: 700;
-}
-
-.reveal a,
-.reveal a:hover {
- text-shadow: 2px 2px 2px #000;
-}
-
-.reveal small a,
-.reveal small a:hover {
- text-shadow: 1px 1px 1px #000;
-}
-
-.reveal p code {
- background-color: $codeBackground;
- display: inline-block;
- border-radius: 7px;
-}
-
-.reveal small code {
- vertical-align: baseline;
-}
\ No newline at end of file
diff --git a/presentation/css/theme/source/league.scss b/presentation/css/theme/source/league.scss
deleted file mode 100644
index 46ea04a0e..000000000
--- a/presentation/css/theme/source/league.scss
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * League theme for reveal.js.
- *
- * This was the default theme pre-3.0.0.
- *
- * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
- */
-
-
-// Default mixins and settings -----------------
-@import "../template/mixins";
-@import "../template/settings";
-// ---------------------------------------------
-
-
-
-// Include theme-specific fonts
-@import url(../../lib/font/league-gothic/league-gothic.css);
-@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);
-
-// Override theme settings (see ../template/settings.scss)
-$headingTextShadow: 0px 0px 6px rgba(0,0,0,0.2);
-$heading1TextShadow: 0 1px 0 #ccc, 0 2px 0 #c9c9c9, 0 3px 0 #bbb, 0 4px 0 #b9b9b9, 0 5px 0 #aaa, 0 6px 1px rgba(0,0,0,.1), 0 0 5px rgba(0,0,0,.1), 0 1px 3px rgba(0,0,0,.3), 0 3px 5px rgba(0,0,0,.2), 0 5px 10px rgba(0,0,0,.25), 0 20px 20px rgba(0,0,0,.15);
-
-// Background generator
-@mixin bodyBackground() {
- @include radial-gradient( rgba(28,30,32,1), rgba(85,90,95,1) );
-}
-
-
-
-// Theme template ------------------------------
-@import "../template/theme";
-// ---------------------------------------------
\ No newline at end of file
diff --git a/presentation/css/theme/source/moon.scss b/presentation/css/theme/source/moon.scss
deleted file mode 100644
index e47e5b562..000000000
--- a/presentation/css/theme/source/moon.scss
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * Solarized Dark theme for reveal.js.
- * Author: Achim Staebler
- */
-
-
-// Default mixins and settings -----------------
-@import "../template/mixins";
-@import "../template/settings";
-// ---------------------------------------------
-
-
-
-// Include theme-specific fonts
-@import url(../../lib/font/league-gothic/league-gothic.css);
-@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);
-
-/**
- * Solarized colors by Ethan Schoonover
- */
-html * {
- color-profile: sRGB;
- rendering-intent: auto;
-}
-
-// Solarized colors
-$base03: #002b36;
-$base02: #073642;
-$base01: #586e75;
-$base00: #657b83;
-$base0: #839496;
-$base1: #93a1a1;
-$base2: #eee8d5;
-$base3: #fdf6e3;
-$yellow: #b58900;
-$orange: #cb4b16;
-$red: #dc322f;
-$magenta: #d33682;
-$violet: #6c71c4;
-$blue: #268bd2;
-$cyan: #2aa198;
-$green: #859900;
-
-// Override theme settings (see ../template/settings.scss)
-$mainColor: $base1;
-$headingColor: $base2;
-$headingTextShadow: none;
-$backgroundColor: $base03;
-$linkColor: $blue;
-$linkColorHover: lighten( $linkColor, 20% );
-$selectionBackgroundColor: $magenta;
-
-
-
-// Theme template ------------------------------
-@import "../template/theme";
-// ---------------------------------------------
diff --git a/presentation/css/theme/source/night.scss b/presentation/css/theme/source/night.scss
deleted file mode 100644
index b0cb57f53..000000000
--- a/presentation/css/theme/source/night.scss
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * Black theme for reveal.js.
- *
- * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
- */
-
-
-// Default mixins and settings -----------------
-@import "../template/mixins";
-@import "../template/settings";
-// ---------------------------------------------
-
-
-// Include theme-specific fonts
-@import url(https://fonts.googleapis.com/css?family=Montserrat:700);
-@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,700,400italic,700italic);
-
-
-// Override theme settings (see ../template/settings.scss)
-$backgroundColor: #111;
-
-$mainFont: 'Open Sans', sans-serif;
-$linkColor: #e7ad52;
-$linkColorHover: lighten( $linkColor, 20% );
-$headingFont: 'Montserrat', Impact, sans-serif;
-$headingTextShadow: none;
-$headingLetterSpacing: -0.03em;
-$headingTextTransform: none;
-$selectionBackgroundColor: #e7ad52;
-$mainFontSize: 30px;
-
-
-// Theme template ------------------------------
-@import "../template/theme";
-// ---------------------------------------------
\ No newline at end of file
diff --git a/presentation/css/theme/source/serif.scss b/presentation/css/theme/source/serif.scss
deleted file mode 100644
index ec3fcb30b..000000000
--- a/presentation/css/theme/source/serif.scss
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * A simple theme for reveal.js presentations, similar
- * to the default theme. The accent color is brown.
- *
- * This theme is Copyright (C) 2012-2013 Owen Versteeg, http://owenversteeg.com - it is MIT licensed.
- */
-
-
-// Default mixins and settings -----------------
-@import "../template/mixins";
-@import "../template/settings";
-// ---------------------------------------------
-
-
-
-// Override theme settings (see ../template/settings.scss)
-$mainFont: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
-$mainColor: #000;
-$headingFont: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif;
-$headingColor: #383D3D;
-$headingTextShadow: none;
-$headingTextTransform: none;
-$backgroundColor: #F0F1EB;
-$linkColor: #51483D;
-$linkColorHover: lighten( $linkColor, 20% );
-$selectionBackgroundColor: #26351C;
-
-.reveal a {
- line-height: 1.3em;
-}
-
-
-// Theme template ------------------------------
-@import "../template/theme";
-// ---------------------------------------------
diff --git a/presentation/css/theme/source/simple.scss b/presentation/css/theme/source/simple.scss
deleted file mode 100644
index 84c7d9b08..000000000
--- a/presentation/css/theme/source/simple.scss
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- * A simple theme for reveal.js presentations, similar
- * to the default theme. The accent color is darkblue.
- *
- * This theme is Copyright (C) 2012 Owen Versteeg, https://github.com/StereotypicalApps. It is MIT licensed.
- * reveal.js is Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
- */
-
-
-// Default mixins and settings -----------------
-@import "../template/mixins";
-@import "../template/settings";
-// ---------------------------------------------
-
-
-
-// Include theme-specific fonts
-@import url(https://fonts.googleapis.com/css?family=News+Cycle:400,700);
-@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);
-
-
-// Override theme settings (see ../template/settings.scss)
-$mainFont: 'Lato', sans-serif;
-$mainColor: #000;
-$headingFont: 'News Cycle', Impact, sans-serif;
-$headingColor: #000;
-$headingTextShadow: none;
-$headingTextTransform: none;
-$backgroundColor: #fff;
-$linkColor: #00008B;
-$linkColorHover: lighten( $linkColor, 20% );
-$selectionBackgroundColor: rgba(0, 0, 0, 0.99);
-
-
-
-// Theme template ------------------------------
-@import "../template/theme";
-// ---------------------------------------------
\ No newline at end of file
diff --git a/presentation/css/theme/source/sky.scss b/presentation/css/theme/source/sky.scss
deleted file mode 100644
index 3fee67c5d..000000000
--- a/presentation/css/theme/source/sky.scss
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * Sky theme for reveal.js.
- *
- * Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
- */
-
-
-// Default mixins and settings -----------------
-@import "../template/mixins";
-@import "../template/settings";
-// ---------------------------------------------
-
-
-
-// Include theme-specific fonts
-@import url(https://fonts.googleapis.com/css?family=Quicksand:400,700,400italic,700italic);
-@import url(https://fonts.googleapis.com/css?family=Open+Sans:400italic,700italic,400,700);
-
-
-// Override theme settings (see ../template/settings.scss)
-$mainFont: 'Open Sans', sans-serif;
-$mainColor: #333;
-$headingFont: 'Quicksand', sans-serif;
-$headingColor: #333;
-$headingLetterSpacing: -0.08em;
-$headingTextShadow: none;
-$backgroundColor: #f7fbfc;
-$linkColor: #3b759e;
-$linkColorHover: lighten( $linkColor, 20% );
-$selectionBackgroundColor: #134674;
-
-// Fix links so they are not cut off
-.reveal a {
- line-height: 1.3em;
-}
-
-// Background generator
-@mixin bodyBackground() {
- @include radial-gradient( #add9e4, #f7fbfc );
-}
-
-
-
-// Theme template ------------------------------
-@import "../template/theme";
-// ---------------------------------------------
diff --git a/presentation/css/theme/source/solarized.scss b/presentation/css/theme/source/solarized.scss
deleted file mode 100644
index 912be56f3..000000000
--- a/presentation/css/theme/source/solarized.scss
+++ /dev/null
@@ -1,63 +0,0 @@
-/**
- * Solarized Light theme for reveal.js.
- * Author: Achim Staebler
- */
-
-
-// Default mixins and settings -----------------
-@import "../template/mixins";
-@import "../template/settings";
-// ---------------------------------------------
-
-
-
-// Include theme-specific fonts
-@import url(../../lib/font/league-gothic/league-gothic.css);
-@import url(https://fonts.googleapis.com/css?family=Lato:400,700,400italic,700italic);
-
-
-/**
- * Solarized colors by Ethan Schoonover
- */
-html * {
- color-profile: sRGB;
- rendering-intent: auto;
-}
-
-// Solarized colors
-$base03: #002b36;
-$base02: #073642;
-$base01: #586e75;
-$base00: #657b83;
-$base0: #839496;
-$base1: #93a1a1;
-$base2: #eee8d5;
-$base3: #fdf6e3;
-$yellow: #b58900;
-$orange: #cb4b16;
-$red: #dc322f;
-$magenta: #d33682;
-$violet: #6c71c4;
-$blue: #268bd2;
-$cyan: #2aa198;
-$green: #859900;
-
-// Override theme settings (see ../template/settings.scss)
-$mainColor: $base00;
-$headingColor: $base01;
-$headingTextShadow: none;
-$backgroundColor: $base3;
-$linkColor: $blue;
-$linkColorHover: lighten( $linkColor, 20% );
-$selectionBackgroundColor: $magenta;
-
-// Background generator
-// @mixin bodyBackground() {
-// @include radial-gradient( rgba($base3,1), rgba(lighten($base3, 20%),1) );
-// }
-
-
-
-// Theme template ------------------------------
-@import "../template/theme";
-// ---------------------------------------------
diff --git a/presentation/css/theme/source/white.scss b/presentation/css/theme/source/white.scss
deleted file mode 100644
index 4c5b647a5..000000000
--- a/presentation/css/theme/source/white.scss
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * White theme for reveal.js. This is the opposite of the 'black' theme.
- *
- * Copyright (C) 2015 Hakim El Hattab, http://hakim.se
- */
-
-
-// Default mixins and settings -----------------
-@import "../template/mixins";
-@import "../template/settings";
-// ---------------------------------------------
-
-
-// Include theme-specific fonts
-@import url(../../lib/font/source-sans-pro/source-sans-pro.css);
-
-
-// Override theme settings (see ../template/settings.scss)
-$backgroundColor: #fff;
-
-$mainColor: #222;
-$headingColor: #222;
-
-$mainFontSize: 38px;
-$mainFont: 'Source Sans Pro', Helvetica, sans-serif;
-$headingFont: 'Source Sans Pro', Helvetica, sans-serif;
-$headingTextShadow: none;
-$headingLetterSpacing: normal;
-$headingTextTransform: uppercase;
-$headingFontWeight: 600;
-$linkColor: #2a76dd;
-$linkColorHover: lighten( $linkColor, 15% );
-$selectionBackgroundColor: lighten( $linkColor, 25% );
-
-$heading1Size: 2.5em;
-$heading2Size: 1.6em;
-$heading3Size: 1.3em;
-$heading4Size: 1.0em;
-
-section.has-dark-background {
- &, h1, h2, h3, h4, h5, h6 {
- color: #fff;
- }
-}
-
-
-// Theme template ------------------------------
-@import "../template/theme";
-// ---------------------------------------------
\ No newline at end of file
diff --git a/presentation/css/theme/template/mixins.scss b/presentation/css/theme/template/mixins.scss
deleted file mode 100644
index e0c560692..000000000
--- a/presentation/css/theme/template/mixins.scss
+++ /dev/null
@@ -1,29 +0,0 @@
-@mixin vertical-gradient( $top, $bottom ) {
- background: $top;
- background: -moz-linear-gradient( top, $top 0%, $bottom 100% );
- background: -webkit-gradient( linear, left top, left bottom, color-stop(0%,$top), color-stop(100%,$bottom) );
- background: -webkit-linear-gradient( top, $top 0%, $bottom 100% );
- background: -o-linear-gradient( top, $top 0%, $bottom 100% );
- background: -ms-linear-gradient( top, $top 0%, $bottom 100% );
- background: linear-gradient( top, $top 0%, $bottom 100% );
-}
-
-@mixin horizontal-gradient( $top, $bottom ) {
- background: $top;
- background: -moz-linear-gradient( left, $top 0%, $bottom 100% );
- background: -webkit-gradient( linear, left top, right top, color-stop(0%,$top), color-stop(100%,$bottom) );
- background: -webkit-linear-gradient( left, $top 0%, $bottom 100% );
- background: -o-linear-gradient( left, $top 0%, $bottom 100% );
- background: -ms-linear-gradient( left, $top 0%, $bottom 100% );
- background: linear-gradient( left, $top 0%, $bottom 100% );
-}
-
-@mixin radial-gradient( $outer, $inner, $type: circle ) {
- background: $outer;
- background: -moz-radial-gradient( center, $type cover, $inner 0%, $outer 100% );
- background: -webkit-gradient( radial, center center, 0px, center center, 100%, color-stop(0%,$inner), color-stop(100%,$outer) );
- background: -webkit-radial-gradient( center, $type cover, $inner 0%, $outer 100% );
- background: -o-radial-gradient( center, $type cover, $inner 0%, $outer 100% );
- background: -ms-radial-gradient( center, $type cover, $inner 0%, $outer 100% );
- background: radial-gradient( center, $type cover, $inner 0%, $outer 100% );
-}
\ No newline at end of file
diff --git a/presentation/css/theme/template/settings.scss b/presentation/css/theme/template/settings.scss
deleted file mode 100644
index ffaac235c..000000000
--- a/presentation/css/theme/template/settings.scss
+++ /dev/null
@@ -1,43 +0,0 @@
-// Base settings for all themes that can optionally be
-// overridden by the super-theme
-
-// Background of the presentation
-$backgroundColor: #2b2b2b;
-
-// Primary/body text
-$mainFont: 'Lato', sans-serif;
-$mainFontSize: 36px;
-$mainColor: #eee;
-
-// Vertical spacing between blocks of text
-$blockMargin: 20px;
-
-// Headings
-$headingMargin: 0 0 $blockMargin 0;
-$headingFont: 'League Gothic', Impact, sans-serif;
-$headingColor: #eee;
-$headingLineHeight: 1.2;
-$headingLetterSpacing: normal;
-$headingTextTransform: uppercase;
-$headingTextShadow: none;
-$headingFontWeight: normal;
-$heading1TextShadow: $headingTextShadow;
-
-$heading1Size: 3.77em;
-$heading2Size: 2.11em;
-$heading3Size: 1.55em;
-$heading4Size: 1.00em;
-
-// Links and actions
-$linkColor: #13DAEC;
-$linkColorHover: lighten( $linkColor, 20% );
-
-// Text selection
-$selectionBackgroundColor: #FF5E99;
-$selectionColor: #fff;
-
-// Generates the presentation background, can be overridden
-// to return a background image or gradient
-@mixin bodyBackground() {
- background: $backgroundColor;
-}
\ No newline at end of file
diff --git a/presentation/css/theme/template/theme.scss b/presentation/css/theme/template/theme.scss
deleted file mode 100644
index d1be10a7c..000000000
--- a/presentation/css/theme/template/theme.scss
+++ /dev/null
@@ -1,339 +0,0 @@
-// Base theme template for reveal.js
-
-/*********************************************
- * GLOBAL STYLES
- *********************************************/
-
-body {
- @include bodyBackground();
- background-color: $backgroundColor;
-}
-
-.reveal {
- font-family: $mainFont;
- font-size: $mainFontSize;
- font-weight: normal;
- color: $mainColor;
-}
-
-::selection {
- color: $selectionColor;
- background: $selectionBackgroundColor;
- text-shadow: none;
-}
-
-.reveal .slides>section,
-.reveal .slides>section>section {
- line-height: 1.3;
- font-weight: inherit;
-}
-
-/*********************************************
- * HEADERS
- *********************************************/
-
-.reveal h1,
-.reveal h2,
-.reveal h3,
-.reveal h4,
-.reveal h5,
-.reveal h6 {
- margin: $headingMargin;
- color: $headingColor;
-
- font-family: $headingFont;
- font-weight: $headingFontWeight;
- line-height: $headingLineHeight;
- letter-spacing: $headingLetterSpacing;
-
- text-transform: $headingTextTransform;
- text-shadow: $headingTextShadow;
-
- word-wrap: break-word;
-}
-
-.reveal h1 {font-size: $heading1Size; }
-.reveal h2 {font-size: $heading2Size; }
-.reveal h3 {font-size: $heading3Size; }
-.reveal h4 {font-size: $heading4Size; }
-
-.reveal h1 {
- text-shadow: $heading1TextShadow;
-}
-
-
-/*********************************************
- * OTHER
- *********************************************/
-
-.reveal p {
- margin: $blockMargin 0;
- line-height: 1.3;
-}
-
-/* Ensure certain elements are never larger than the slide itself */
-.reveal img,
-.reveal video,
-.reveal iframe {
- max-width: 95%;
- max-height: 95%;
-}
-.reveal strong,
-.reveal b {
- font-weight: bold;
-}
-
-.reveal em {
- font-style: italic;
-}
-
-.reveal ol,
-.reveal dl,
-.reveal ul {
- display: inline-block;
-
- text-align: left;
- margin: 0 0 0 1em;
-}
-
-.reveal ol {
- list-style-type: decimal;
-}
-
-.reveal ul {
- list-style-type: disc;
-}
-
-.reveal ul ul {
- list-style-type: square;
-}
-
-.reveal ul ul ul {
- list-style-type: circle;
-}
-
-.reveal ul ul,
-.reveal ul ol,
-.reveal ol ol,
-.reveal ol ul {
- display: block;
- margin-left: 40px;
-}
-
-.reveal dt {
- font-weight: bold;
-}
-
-.reveal dd {
- margin-left: 40px;
-}
-
-.reveal q,
-.reveal blockquote {
- quotes: none;
-}
-
-.reveal blockquote {
- display: block;
- position: relative;
- width: 70%;
- margin: $blockMargin auto;
- padding: 5px;
-
- font-style: italic;
- background: rgba(255, 255, 255, 0.05);
- box-shadow: 0px 0px 2px rgba(0,0,0,0.2);
-}
- .reveal blockquote p:first-child,
- .reveal blockquote p:last-child {
- display: inline-block;
- }
-
-.reveal q {
- font-style: italic;
-}
-
-.reveal pre {
- display: block;
- position: relative;
- width: 90%;
- margin: $blockMargin auto;
-
- text-align: left;
- font-size: 0.55em;
- font-family: monospace;
- line-height: 1.2em;
-
- word-wrap: break-word;
-
- box-shadow: 0px 0px 6px rgba(0,0,0,0.3);
-}
-.reveal code {
- font-family: monospace;
-}
-
-.reveal pre code {
- display: block;
- padding: 5px;
- overflow: auto;
- max-height: 400px;
- word-wrap: normal;
- background: #3F3F3F;
- color: #DCDCDC;
-}
-
-.reveal table {
- margin: auto;
- border-collapse: collapse;
- border-spacing: 0;
-}
-
-.reveal table th {
- font-weight: bold;
-}
-
-.reveal table th,
-.reveal table td {
- text-align: left;
- padding: 0.2em 0.5em 0.2em 0.5em;
- border-bottom: 1px solid;
-}
-
-.reveal table tr:last-child td {
- border-bottom: none;
-}
-
-.reveal sup {
- vertical-align: super;
-}
-.reveal sub {
- vertical-align: sub;
-}
-
-.reveal small {
- display: inline-block;
- font-size: 0.6em;
- line-height: 1.2em;
- vertical-align: top;
-}
-
-.reveal small * {
- vertical-align: top;
-}
-
-
-/*********************************************
- * LINKS
- *********************************************/
-
-.reveal a {
- color: $linkColor;
- text-decoration: none;
-
- -webkit-transition: color .15s ease;
- -moz-transition: color .15s ease;
- transition: color .15s ease;
-}
- .reveal a:hover {
- color: $linkColorHover;
-
- text-shadow: none;
- border: none;
- }
-
-.reveal .roll span:after {
- color: #fff;
- background: darken( $linkColor, 15% );
-}
-
-
-/*********************************************
- * IMAGES
- *********************************************/
-
-.reveal section img {
- margin: 15px 0px;
- background: rgba(255,255,255,0.12);
- border: 4px solid $mainColor;
-
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.15);
-}
-
- .reveal a img {
- -webkit-transition: all .15s linear;
- -moz-transition: all .15s linear;
- transition: all .15s linear;
- }
-
- .reveal a:hover img {
- background: rgba(255,255,255,0.2);
- border-color: $linkColor;
-
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.55);
- }
-
-
-/*********************************************
- * NAVIGATION CONTROLS
- *********************************************/
-
-.reveal .controls div.navigate-left,
-.reveal .controls div.navigate-left.enabled {
- border-right-color: $linkColor;
-}
-
-.reveal .controls div.navigate-right,
-.reveal .controls div.navigate-right.enabled {
- border-left-color: $linkColor;
-}
-
-.reveal .controls div.navigate-up,
-.reveal .controls div.navigate-up.enabled {
- border-bottom-color: $linkColor;
-}
-
-.reveal .controls div.navigate-down,
-.reveal .controls div.navigate-down.enabled {
- border-top-color: $linkColor;
-}
-
-.reveal .controls div.navigate-left.enabled:hover {
- border-right-color: $linkColorHover;
-}
-
-.reveal .controls div.navigate-right.enabled:hover {
- border-left-color: $linkColorHover;
-}
-
-.reveal .controls div.navigate-up.enabled:hover {
- border-bottom-color: $linkColorHover;
-}
-
-.reveal .controls div.navigate-down.enabled:hover {
- border-top-color: $linkColorHover;
-}
-
-
-/*********************************************
- * PROGRESS BAR
- *********************************************/
-
-.reveal .progress {
- background: rgba(0,0,0,0.2);
-}
- .reveal .progress span {
- background: $linkColor;
-
- -webkit-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985);
- -moz-transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985);
- transition: width 800ms cubic-bezier(0.260, 0.860, 0.440, 0.985);
- }
-
-/*********************************************
- * SLIDE NUMBER
- *********************************************/
-.reveal .slide-number {
- color: $linkColor;
-}
-
-
diff --git a/presentation/css/theme/white.css b/presentation/css/theme/white.css
deleted file mode 100644
index 5fdb2fcbe..000000000
--- a/presentation/css/theme/white.css
+++ /dev/null
@@ -1,261 +0,0 @@
-@import url(../../lib/font/source-sans-pro/source-sans-pro.css);
-/**
- * White theme for reveal.js. This is the opposite of the 'black' theme.
- *
- * Copyright (C) 2015 Hakim El Hattab, http://hakim.se
- */
-section.has-dark-background, section.has-dark-background h1, section.has-dark-background h2, section.has-dark-background h3, section.has-dark-background h4, section.has-dark-background h5, section.has-dark-background h6 {
- color: #fff; }
-
-/*********************************************
- * GLOBAL STYLES
- *********************************************/
-body {
- background: #fff;
- background-color: #fff; }
-
-.reveal {
- font-family: 'Source Sans Pro', Helvetica, sans-serif;
- font-size: 38px;
- font-weight: normal;
- color: #222; }
-
-::selection {
- color: #fff;
- background: #98bdef;
- text-shadow: none; }
-
-.reveal .slides > section, .reveal .slides > section > section {
- line-height: 1.3;
- font-weight: inherit; }
-
-/*********************************************
- * HEADERS
- *********************************************/
-.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {
- margin: 0 0 20px 0;
- color: #222;
- font-family: 'Source Sans Pro', Helvetica, sans-serif;
- font-weight: 600;
- line-height: 1.2;
- letter-spacing: normal;
- text-transform: uppercase;
- text-shadow: none;
- word-wrap: break-word; }
-
-.reveal h1 {
- font-size: 2.5em; }
-
-.reveal h2 {
- font-size: 1.6em; }
-
-.reveal h3 {
- font-size: 1.3em; }
-
-.reveal h4 {
- font-size: 1em; }
-
-.reveal h1 {
- text-shadow: none; }
-
-/*********************************************
- * OTHER
- *********************************************/
-.reveal p {
- margin: 20px 0;
- line-height: 1.3; }
-
-/* Ensure certain elements are never larger than the slide itself */
-.reveal img, .reveal video, .reveal iframe {
- max-width: 95%;
- max-height: 95%; }
-
-.reveal strong, .reveal b {
- font-weight: bold; }
-
-.reveal em {
- font-style: italic; }
-
-.reveal ol, .reveal dl, .reveal ul {
- display: inline-block;
- text-align: left;
- margin: 0 0 0 1em; }
-
-.reveal ol {
- list-style-type: decimal; }
-
-.reveal ul {
- list-style-type: disc; }
-
-.reveal ul ul {
- list-style-type: square; }
-
-.reveal ul ul ul {
- list-style-type: circle; }
-
-.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul {
- display: block;
- margin-left: 40px; }
-
-.reveal dt {
- font-weight: bold; }
-
-.reveal dd {
- margin-left: 40px; }
-
-.reveal q, .reveal blockquote {
- quotes: none; }
-
-.reveal blockquote {
- display: block;
- position: relative;
- width: 70%;
- margin: 20px auto;
- padding: 5px;
- font-style: italic;
- background: rgba(255, 255, 255, 0.05);
- box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); }
-
-.reveal blockquote p:first-child, .reveal blockquote p:last-child {
- display: inline-block; }
-
-.reveal q {
- font-style: italic; }
-
-.reveal pre {
- display: block;
- position: relative;
- width: 90%;
- margin: 20px auto;
- text-align: left;
- font-size: 0.55em;
- font-family: monospace;
- line-height: 1.2em;
- word-wrap: break-word;
- box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
-
-.reveal code {
- font-family: monospace; }
-
-.reveal pre code {
- display: block;
- padding: 5px;
- overflow: auto;
- max-height: 400px;
- word-wrap: normal;
- background: #3F3F3F;
- color: #DCDCDC; }
-
-.reveal table {
- margin: auto;
- border-collapse: collapse;
- border-spacing: 0; }
-
-.reveal table th {
- font-weight: bold; }
-
-.reveal table th, .reveal table td {
- text-align: left;
- padding: 0.2em 0.5em 0.2em 0.5em;
- border-bottom: 1px solid; }
-
-.reveal table tr:last-child td {
- border-bottom: none; }
-
-.reveal sup {
- vertical-align: super; }
-
-.reveal sub {
- vertical-align: sub; }
-
-.reveal small {
- display: inline-block;
- font-size: 0.6em;
- line-height: 1.2em;
- vertical-align: top; }
-
-.reveal small * {
- vertical-align: top; }
-
-/*********************************************
- * LINKS
- *********************************************/
-.reveal a {
- color: #2a76dd;
- text-decoration: none;
- -webkit-transition: color 0.15s ease;
- -moz-transition: color 0.15s ease;
- transition: color 0.15s ease; }
-
-.reveal a:hover {
- color: #6ca2e8;
- text-shadow: none;
- border: none; }
-
-.reveal .roll span:after {
- color: #fff;
- background: #1a54a1; }
-
-/*********************************************
- * IMAGES
- *********************************************/
-.reveal section img {
- margin: 15px 0px;
- background: rgba(255, 255, 255, 0.12);
- border: 4px solid #222;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); }
-
-.reveal a img {
- -webkit-transition: all 0.15s linear;
- -moz-transition: all 0.15s linear;
- transition: all 0.15s linear; }
-
-.reveal a:hover img {
- background: rgba(255, 255, 255, 0.2);
- border-color: #2a76dd;
- box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); }
-
-/*********************************************
- * NAVIGATION CONTROLS
- *********************************************/
-.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled {
- border-right-color: #2a76dd; }
-
-.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled {
- border-left-color: #2a76dd; }
-
-.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled {
- border-bottom-color: #2a76dd; }
-
-.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled {
- border-top-color: #2a76dd; }
-
-.reveal .controls div.navigate-left.enabled:hover {
- border-right-color: #6ca2e8; }
-
-.reveal .controls div.navigate-right.enabled:hover {
- border-left-color: #6ca2e8; }
-
-.reveal .controls div.navigate-up.enabled:hover {
- border-bottom-color: #6ca2e8; }
-
-.reveal .controls div.navigate-down.enabled:hover {
- border-top-color: #6ca2e8; }
-
-/*********************************************
- * PROGRESS BAR
- *********************************************/
-.reveal .progress {
- background: rgba(0, 0, 0, 0.2); }
-
-.reveal .progress span {
- background: #2a76dd;
- -webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- -moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
- transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
-
-/*********************************************
- * SLIDE NUMBER
- *********************************************/
-.reveal .slide-number {
- color: #2a76dd; }
diff --git a/presentation/img/kitten.jpg b/presentation/img/kitten.jpg
deleted file mode 100644
index cb90ef944..000000000
Binary files a/presentation/img/kitten.jpg and /dev/null differ
diff --git a/presentation/img/pony.png b/presentation/img/pony.png
deleted file mode 100644
index a34b1e5c3..000000000
Binary files a/presentation/img/pony.png and /dev/null differ
diff --git a/presentation/img/repo.svg b/presentation/img/repo.svg
deleted file mode 100644
index d4ad44df8..000000000
--- a/presentation/img/repo.svg
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/presentation/img/stack.jpg b/presentation/img/stack.jpg
deleted file mode 100644
index 4d8fb1e3c..000000000
Binary files a/presentation/img/stack.jpg and /dev/null differ
diff --git a/presentation/index.html b/presentation/index.html
deleted file mode 100644
index 4bb4b7cf5..000000000
--- a/presentation/index.html
+++ /dev/null
@@ -1,220 +0,0 @@
-
-
-
-
-
-
- Paperless
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- My Inspiration
-
-
-
-
-
-
- Paper Loves Being Mysterious
- There's No Ctrl+F
-
-
- Paper Hates Your Spine
- It's heavy and takes up space
-
-
- Paper Wants to Breed
- Backups? More Paper!
-
-
- Paper Hates the Planet
-
-
-
-
- It's 2016
- We shouldn't even need to use this stuff anymore
- So I made Paperless
-
-
-
- What's it Do?
-
- Consumes documents from a directory, API, or mailbox
- OCRs them and puts the text in a database
- Automatically tags the db record based on user-rules
- Encrypts and stores the original
-
-
-
-
- It's Just a Wrapper
-
- ImageMagick
- Tesseract
- GnuPG
- Python3/Django
-
-
-
-
- How's it Work?
-
-
- 1: The Consumer
- (Imports documents)
-
- Runs in a loop
- Reads from a directory
- Occasionally checking a mailbox
-
-
-
- 2: The Webserver
- (Lets you find stuff)
-
- Currently the Django Admin (I know)
- Provides a DRF API
- Users are building proper UIs
-
-
-
-
-
- Demo!
-
-
-
-
- TODO
- It works, but it needs polish
-
- The UI is the Django admin
- Mail consumption is really raw
- Some sort of plugin architecture?
- OMG Tests
-
-
-
-
- Plugin architecture : there've been requests
- for some overly custom stuff to happen before and after
- consumption, but in the UNIX spirit of "do one job well", I
- think this sort of thing is better written as a plugin -- which
- means I need to figure out a best practise for that.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/presentation/js/reveal.js b/presentation/js/reveal.js
deleted file mode 100644
index 65ac29ffd..000000000
--- a/presentation/js/reveal.js
+++ /dev/null
@@ -1,4276 +0,0 @@
-/*!
- * reveal.js
- * http://lab.hakim.se/reveal-js
- * MIT licensed
- *
- * Copyright (C) 2015 Hakim El Hattab, http://hakim.se
- */
-(function( root, factory ) {
- if( typeof define === 'function' && define.amd ) {
- // AMD. Register as an anonymous module.
- define( function() {
- root.Reveal = factory();
- return root.Reveal;
- } );
- } else if( typeof exports === 'object' ) {
- // Node. Does not work with strict CommonJS.
- module.exports = factory();
- } else {
- // Browser globals.
- root.Reveal = factory();
- }
-}( this, function() {
-
- 'use strict';
-
- var Reveal;
-
- var SLIDES_SELECTOR = '.slides section',
- HORIZONTAL_SLIDES_SELECTOR = '.slides>section',
- VERTICAL_SLIDES_SELECTOR = '.slides>section.present>section',
- HOME_SLIDE_SELECTOR = '.slides>section:first-of-type',
-
- // Configurations defaults, can be overridden at initialization time
- config = {
-
- // The "normal" size of the presentation, aspect ratio will be preserved
- // when the presentation is scaled to fit different resolutions
- width: 960,
- height: 700,
-
- // Factor of the display size that should remain empty around the content
- margin: 0.1,
-
- // Bounds for smallest/largest possible scale to apply to content
- minScale: 0.2,
- maxScale: 1.5,
-
- // Display controls in the bottom right corner
- controls: true,
-
- // Display a presentation progress bar
- progress: true,
-
- // Display the page number of the current slide
- slideNumber: false,
-
- // Push each slide change to the browser history
- history: false,
-
- // Enable keyboard shortcuts for navigation
- keyboard: true,
-
- // Optional function that blocks keyboard events when retuning false
- keyboardCondition: null,
-
- // Enable the slide overview mode
- overview: true,
-
- // Vertical centering of slides
- center: true,
-
- // Enables touch navigation on devices with touch input
- touch: true,
-
- // Loop the presentation
- loop: false,
-
- // Change the presentation direction to be RTL
- rtl: false,
-
- // Turns fragments on and off globally
- fragments: true,
-
- // Flags if the presentation is running in an embedded mode,
- // i.e. contained within a limited portion of the screen
- embedded: false,
-
- // Flags if we should show a help overlay when the questionmark
- // key is pressed
- help: true,
-
- // Flags if it should be possible to pause the presentation (blackout)
- pause: true,
-
- // Number of milliseconds between automatically proceeding to the
- // next slide, disabled when set to 0, this value can be overwritten
- // by using a data-autoslide attribute on your slides
- autoSlide: 0,
-
- // Stop auto-sliding after user input
- autoSlideStoppable: true,
-
- // Enable slide navigation via mouse wheel
- mouseWheel: false,
-
- // Apply a 3D roll to links on hover
- rollingLinks: false,
-
- // Hides the address bar on mobile devices
- hideAddressBar: true,
-
- // Opens links in an iframe preview overlay
- previewLinks: false,
-
- // Exposes the reveal.js API through window.postMessage
- postMessage: true,
-
- // Dispatches all reveal.js events to the parent window through postMessage
- postMessageEvents: false,
-
- // Focuses body when page changes visiblity to ensure keyboard shortcuts work
- focusBodyOnPageVisibilityChange: true,
-
- // Transition style
- transition: 'slide', // none/fade/slide/convex/concave/zoom
-
- // Transition speed
- transitionSpeed: 'default', // default/fast/slow
-
- // Transition style for full page slide backgrounds
- backgroundTransition: 'fade', // none/fade/slide/convex/concave/zoom
-
- // Parallax background image
- parallaxBackgroundImage: '', // CSS syntax, e.g. "a.jpg"
-
- // Parallax background size
- parallaxBackgroundSize: '', // CSS syntax, e.g. "3000px 2000px"
-
- // Number of slides away from the current that are visible
- viewDistance: 3,
-
- // Script dependencies to load
- dependencies: []
-
- },
-
- // Flags if reveal.js is loaded (has dispatched the 'ready' event)
- loaded = false,
-
- // The horizontal and vertical index of the currently active slide
- indexh,
- indexv,
-
- // The previous and current slide HTML elements
- previousSlide,
- currentSlide,
-
- previousBackground,
-
- // Slides may hold a data-state attribute which we pick up and apply
- // as a class to the body. This list contains the combined state of
- // all current slides.
- state = [],
-
- // The current scale of the presentation (see width/height config)
- scale = 1,
-
- // Cached references to DOM elements
- dom = {},
-
- // Features supported by the browser, see #checkCapabilities()
- features = {},
-
- // Client is a mobile device, see #checkCapabilities()
- isMobileDevice,
-
- // Throttles mouse wheel navigation
- lastMouseWheelStep = 0,
-
- // Delays updates to the URL due to a Chrome thumbnailer bug
- writeURLTimeout = 0,
-
- // Flags if the interaction event listeners are bound
- eventsAreBound = false,
-
- // The current auto-slide duration
- autoSlide = 0,
-
- // Auto slide properties
- autoSlidePlayer,
- autoSlideTimeout = 0,
- autoSlideStartTime = -1,
- autoSlidePaused = false,
-
- // Holds information about the currently ongoing touch input
- touch = {
- startX: 0,
- startY: 0,
- startSpan: 0,
- startCount: 0,
- captured: false,
- threshold: 40
- },
-
- // Holds information about the keyboard shortcuts
- keyboardShortcuts = {
- 'N , SPACE': 'Next slide',
- 'P': 'Previous slide',
- '← , H': 'Navigate left',
- '→ , L': 'Navigate right',
- '↑ , K': 'Navigate up',
- '↓ , J': 'Navigate down',
- 'Home': 'First slide',
- 'End': 'Last slide',
- 'B , .': 'Pause',
- 'F': 'Fullscreen',
- 'ESC, O': 'Slide overview'
- };
-
- /**
- * Starts up the presentation if the client is capable.
- */
- function initialize( options ) {
-
- checkCapabilities();
-
- if( !features.transforms2d && !features.transforms3d ) {
- document.body.setAttribute( 'class', 'no-transforms' );
-
- // Since JS won't be running any further, we need to load all
- // images that were intended to lazy load now
- var images = document.getElementsByTagName( 'img' );
- for( var i = 0, len = images.length; i < len; i++ ) {
- var image = images[i];
- if( image.getAttribute( 'data-src' ) ) {
- image.setAttribute( 'src', image.getAttribute( 'data-src' ) );
- image.removeAttribute( 'data-src' );
- }
- }
-
- // If the browser doesn't support core features we won't be
- // using JavaScript to control the presentation
- return;
- }
-
- // Cache references to key DOM elements
- dom.wrapper = document.querySelector( '.reveal' );
- dom.slides = document.querySelector( '.reveal .slides' );
-
- // Force a layout when the whole page, incl fonts, has loaded
- window.addEventListener( 'load', layout, false );
-
- var query = Reveal.getQueryHash();
-
- // Do not accept new dependencies via query config to avoid
- // the potential of malicious script injection
- if( typeof query['dependencies'] !== 'undefined' ) delete query['dependencies'];
-
- // Copy options over to our config object
- extend( config, options );
- extend( config, query );
-
- // Hide the address bar in mobile browsers
- hideAddressBar();
-
- // Loads the dependencies and continues to #start() once done
- load();
-
- }
-
- /**
- * Inspect the client to see what it's capable of, this
- * should only happens once per runtime.
- */
- function checkCapabilities() {
-
- features.transforms3d = 'WebkitPerspective' in document.body.style ||
- 'MozPerspective' in document.body.style ||
- 'msPerspective' in document.body.style ||
- 'OPerspective' in document.body.style ||
- 'perspective' in document.body.style;
-
- features.transforms2d = 'WebkitTransform' in document.body.style ||
- 'MozTransform' in document.body.style ||
- 'msTransform' in document.body.style ||
- 'OTransform' in document.body.style ||
- 'transform' in document.body.style;
-
- features.requestAnimationFrameMethod = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame;
- features.requestAnimationFrame = typeof features.requestAnimationFrameMethod === 'function';
-
- features.canvas = !!document.createElement( 'canvas' ).getContext;
-
- features.touch = !!( 'ontouchstart' in window );
-
- isMobileDevice = navigator.userAgent.match( /(iphone|ipod|ipad|android)/gi );
-
- }
-
- /**
- * Loads the dependencies of reveal.js. Dependencies are
- * defined via the configuration option 'dependencies'
- * and will be loaded prior to starting/binding reveal.js.
- * Some dependencies may have an 'async' flag, if so they
- * will load after reveal.js has been started up.
- */
- function load() {
-
- var scripts = [],
- scriptsAsync = [],
- scriptsToPreload = 0;
-
- // Called once synchronous scripts finish loading
- function proceed() {
- if( scriptsAsync.length ) {
- // Load asynchronous scripts
- head.js.apply( null, scriptsAsync );
- }
-
- start();
- }
-
- function loadScript( s ) {
- head.ready( s.src.match( /([\w\d_\-]*)\.?js$|[^\\\/]*$/i )[0], function() {
- // Extension may contain callback functions
- if( typeof s.callback === 'function' ) {
- s.callback.apply( this );
- }
-
- if( --scriptsToPreload === 0 ) {
- proceed();
- }
- });
- }
-
- for( var i = 0, len = config.dependencies.length; i < len; i++ ) {
- var s = config.dependencies[i];
-
- // Load if there's no condition or the condition is truthy
- if( !s.condition || s.condition() ) {
- if( s.async ) {
- scriptsAsync.push( s.src );
- }
- else {
- scripts.push( s.src );
- }
-
- loadScript( s );
- }
- }
-
- if( scripts.length ) {
- scriptsToPreload = scripts.length;
-
- // Load synchronous scripts
- head.js.apply( null, scripts );
- }
- else {
- proceed();
- }
-
- }
-
- /**
- * Starts up reveal.js by binding input events and navigating
- * to the current URL deeplink if there is one.
- */
- function start() {
-
- // Make sure we've got all the DOM elements we need
- setupDOM();
-
- // Listen to messages posted to this window
- setupPostMessage();
-
- // Resets all vertical slides so that only the first is visible
- resetVerticalSlides();
-
- // Updates the presentation to match the current configuration values
- configure();
-
- // Read the initial hash
- readURL();
-
- // Update all backgrounds
- updateBackground( true );
-
- // Notify listeners that the presentation is ready but use a 1ms
- // timeout to ensure it's not fired synchronously after #initialize()
- setTimeout( function() {
- // Enable transitions now that we're loaded
- dom.slides.classList.remove( 'no-transition' );
-
- loaded = true;
-
- dispatchEvent( 'ready', {
- 'indexh': indexh,
- 'indexv': indexv,
- 'currentSlide': currentSlide
- } );
- }, 1 );
-
- // Special setup and config is required when printing to PDF
- if( isPrintingPDF() ) {
- removeEventListeners();
-
- // The document needs to have loaded for the PDF layout
- // measurements to be accurate
- if( document.readyState === 'complete' ) {
- setupPDF();
- }
- else {
- window.addEventListener( 'load', setupPDF );
- }
- }
-
- }
-
- /**
- * Finds and stores references to DOM elements which are
- * required by the presentation. If a required element is
- * not found, it is created.
- */
- function setupDOM() {
-
- // Prevent transitions while we're loading
- dom.slides.classList.add( 'no-transition' );
-
- // Background element
- dom.background = createSingletonNode( dom.wrapper, 'div', 'backgrounds', null );
-
- // Progress bar
- dom.progress = createSingletonNode( dom.wrapper, 'div', 'progress', ' ' );
- dom.progressbar = dom.progress.querySelector( 'span' );
-
- // Arrow controls
- createSingletonNode( dom.wrapper, 'aside', 'controls',
- '
' +
- '
' +
- '
' +
- '
' );
-
- // Slide number
- dom.slideNumber = createSingletonNode( dom.wrapper, 'div', 'slide-number', '' );
-
- // Overlay graphic which is displayed during the paused mode
- createSingletonNode( dom.wrapper, 'div', 'pause-overlay', null );
-
- // Cache references to elements
- dom.controls = document.querySelector( '.reveal .controls' );
- dom.theme = document.querySelector( '#theme' );
-
- dom.wrapper.setAttribute( 'role', 'application' );
-
- // There can be multiple instances of controls throughout the page
- dom.controlsLeft = toArray( document.querySelectorAll( '.navigate-left' ) );
- dom.controlsRight = toArray( document.querySelectorAll( '.navigate-right' ) );
- dom.controlsUp = toArray( document.querySelectorAll( '.navigate-up' ) );
- dom.controlsDown = toArray( document.querySelectorAll( '.navigate-down' ) );
- dom.controlsPrev = toArray( document.querySelectorAll( '.navigate-prev' ) );
- dom.controlsNext = toArray( document.querySelectorAll( '.navigate-next' ) );
-
- dom.statusDiv = createStatusDiv();
- }
-
- /**
- * Creates a hidden div with role aria-live to announce the
- * current slide content. Hide the div off-screen to make it
- * available only to Assistive Technologies.
- */
- function createStatusDiv() {
-
- var statusDiv = document.getElementById( 'aria-status-div' );
- if( !statusDiv ) {
- statusDiv = document.createElement( 'div' );
- statusDiv.style.position = 'absolute';
- statusDiv.style.height = '1px';
- statusDiv.style.width = '1px';
- statusDiv.style.overflow ='hidden';
- statusDiv.style.clip = 'rect( 1px, 1px, 1px, 1px )';
- statusDiv.setAttribute( 'id', 'aria-status-div' );
- statusDiv.setAttribute( 'aria-live', 'polite' );
- statusDiv.setAttribute( 'aria-atomic','true' );
- dom.wrapper.appendChild( statusDiv );
- }
- return statusDiv;
-
- }
-
- /**
- * Configures the presentation for printing to a static
- * PDF.
- */
- function setupPDF() {
-
- var slideSize = getComputedSlideSize( window.innerWidth, window.innerHeight );
-
- // Dimensions of the PDF pages
- var pageWidth = Math.floor( slideSize.width * ( 1 + config.margin ) ),
- pageHeight = Math.floor( slideSize.height * ( 1 + config.margin ) );
-
- // Dimensions of slides within the pages
- var slideWidth = slideSize.width,
- slideHeight = slideSize.height;
-
- // Let the browser know what page size we want to print
- injectStyleSheet( '@page{size:'+ pageWidth +'px '+ pageHeight +'px; margin: 0;}' );
-
- // Limit the size of certain elements to the dimensions of the slide
- injectStyleSheet( '.reveal section>img, .reveal section>video, .reveal section>iframe{max-width: '+ slideWidth +'px; max-height:'+ slideHeight +'px}' );
-
- document.body.classList.add( 'print-pdf' );
- document.body.style.width = pageWidth + 'px';
- document.body.style.height = pageHeight + 'px';
-
- // Slide and slide background layout
- toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) {
-
- // Vertical stacks are not centred since their section
- // children will be
- if( slide.classList.contains( 'stack' ) === false ) {
- // Center the slide inside of the page, giving the slide some margin
- var left = ( pageWidth - slideWidth ) / 2,
- top = ( pageHeight - slideHeight ) / 2;
-
- var contentHeight = getAbsoluteHeight( slide );
- var numberOfPages = Math.max( Math.ceil( contentHeight / pageHeight ), 1 );
-
- // Center slides vertically
- if( numberOfPages === 1 && config.center || slide.classList.contains( 'center' ) ) {
- top = Math.max( ( pageHeight - contentHeight ) / 2, 0 );
- }
-
- // Position the slide inside of the page
- slide.style.left = left + 'px';
- slide.style.top = top + 'px';
- slide.style.width = slideWidth + 'px';
-
- // TODO Backgrounds need to be multiplied when the slide
- // stretches over multiple pages
- var background = slide.querySelector( '.slide-background' );
- if( background ) {
- background.style.width = pageWidth + 'px';
- background.style.height = ( pageHeight * numberOfPages ) + 'px';
- background.style.top = -top + 'px';
- background.style.left = -left + 'px';
- }
- }
-
- } );
-
- // Show all fragments
- toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ' .fragment' ) ).forEach( function( fragment ) {
- fragment.classList.add( 'visible' );
- } );
-
- }
-
- /**
- * Creates an HTML element and returns a reference to it.
- * If the element already exists the existing instance will
- * be returned.
- */
- function createSingletonNode( container, tagname, classname, innerHTML ) {
-
- // Find all nodes matching the description
- var nodes = container.querySelectorAll( '.' + classname );
-
- // Check all matches to find one which is a direct child of
- // the specified container
- for( var i = 0; i < nodes.length; i++ ) {
- var testNode = nodes[i];
- if( testNode.parentNode === container ) {
- return testNode;
- }
- }
-
- // If no node was found, create it now
- var node = document.createElement( tagname );
- node.classList.add( classname );
- if( typeof innerHTML === 'string' ) {
- node.innerHTML = innerHTML;
- }
- container.appendChild( node );
-
- return node;
-
- }
-
- /**
- * Creates the slide background elements and appends them
- * to the background container. One element is created per
- * slide no matter if the given slide has visible background.
- */
- function createBackgrounds() {
-
- var printMode = isPrintingPDF();
-
- // Clear prior backgrounds
- dom.background.innerHTML = '';
- dom.background.classList.add( 'no-transition' );
-
- // Iterate over all horizontal slides
- toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).forEach( function( slideh ) {
-
- var backgroundStack;
-
- if( printMode ) {
- backgroundStack = createBackground( slideh, slideh );
- }
- else {
- backgroundStack = createBackground( slideh, dom.background );
- }
-
- // Iterate over all vertical slides
- toArray( slideh.querySelectorAll( 'section' ) ).forEach( function( slidev ) {
-
- if( printMode ) {
- createBackground( slidev, slidev );
- }
- else {
- createBackground( slidev, backgroundStack );
- }
-
- backgroundStack.classList.add( 'stack' );
-
- } );
-
- } );
-
- // Add parallax background if specified
- if( config.parallaxBackgroundImage ) {
-
- dom.background.style.backgroundImage = 'url("' + config.parallaxBackgroundImage + '")';
- dom.background.style.backgroundSize = config.parallaxBackgroundSize;
-
- // Make sure the below properties are set on the element - these properties are
- // needed for proper transitions to be set on the element via CSS. To remove
- // annoying background slide-in effect when the presentation starts, apply
- // these properties after short time delay
- setTimeout( function() {
- dom.wrapper.classList.add( 'has-parallax-background' );
- }, 1 );
-
- }
- else {
-
- dom.background.style.backgroundImage = '';
- dom.wrapper.classList.remove( 'has-parallax-background' );
-
- }
-
- }
-
- /**
- * Creates a background for the given slide.
- *
- * @param {HTMLElement} slide
- * @param {HTMLElement} container The element that the background
- * should be appended to
- */
- function createBackground( slide, container ) {
-
- var data = {
- background: slide.getAttribute( 'data-background' ),
- backgroundSize: slide.getAttribute( 'data-background-size' ),
- backgroundImage: slide.getAttribute( 'data-background-image' ),
- backgroundVideo: slide.getAttribute( 'data-background-video' ),
- backgroundIframe: slide.getAttribute( 'data-background-iframe' ),
- backgroundColor: slide.getAttribute( 'data-background-color' ),
- backgroundRepeat: slide.getAttribute( 'data-background-repeat' ),
- backgroundPosition: slide.getAttribute( 'data-background-position' ),
- backgroundTransition: slide.getAttribute( 'data-background-transition' )
- };
-
- var element = document.createElement( 'div' );
-
- // Carry over custom classes from the slide to the background
- element.className = 'slide-background ' + slide.className.replace( /present|past|future/, '' );
-
- if( data.background ) {
- // Auto-wrap image urls in url(...)
- if( /^(http|file|\/\/)/gi.test( data.background ) || /\.(svg|png|jpg|jpeg|gif|bmp)$/gi.test( data.background ) ) {
- slide.setAttribute( 'data-background-image', data.background );
- }
- else {
- element.style.background = data.background;
- }
- }
-
- // Create a hash for this combination of background settings.
- // This is used to determine when two slide backgrounds are
- // the same.
- if( data.background || data.backgroundColor || data.backgroundImage || data.backgroundVideo || data.backgroundIframe ) {
- element.setAttribute( 'data-background-hash', data.background +
- data.backgroundSize +
- data.backgroundImage +
- data.backgroundVideo +
- data.backgroundIframe +
- data.backgroundColor +
- data.backgroundRepeat +
- data.backgroundPosition +
- data.backgroundTransition );
- }
-
- // Additional and optional background properties
- if( data.backgroundSize ) element.style.backgroundSize = data.backgroundSize;
- if( data.backgroundColor ) element.style.backgroundColor = data.backgroundColor;
- if( data.backgroundRepeat ) element.style.backgroundRepeat = data.backgroundRepeat;
- if( data.backgroundPosition ) element.style.backgroundPosition = data.backgroundPosition;
- if( data.backgroundTransition ) element.setAttribute( 'data-background-transition', data.backgroundTransition );
-
- container.appendChild( element );
-
- // If backgrounds are being recreated, clear old classes
- slide.classList.remove( 'has-dark-background' );
- slide.classList.remove( 'has-light-background' );
-
- // If this slide has a background color, add a class that
- // signals if it is light or dark. If the slide has no background
- // color, no class will be set
- var computedBackgroundColor = window.getComputedStyle( element ).backgroundColor;
- if( computedBackgroundColor ) {
- var rgb = colorToRgb( computedBackgroundColor );
-
- // Ignore fully transparent backgrounds. Some browsers return
- // rgba(0,0,0,0) when reading the computed background color of
- // an element with no background
- if( rgb && rgb.a !== 0 ) {
- if( colorBrightness( computedBackgroundColor ) < 128 ) {
- slide.classList.add( 'has-dark-background' );
- }
- else {
- slide.classList.add( 'has-light-background' );
- }
- }
- }
-
- return element;
-
- }
-
- /**
- * Registers a listener to postMessage events, this makes it
- * possible to call all reveal.js API methods from another
- * window. For example:
- *
- * revealWindow.postMessage( JSON.stringify({
- * method: 'slide',
- * args: [ 2 ]
- * }), '*' );
- */
- function setupPostMessage() {
-
- if( config.postMessage ) {
- window.addEventListener( 'message', function ( event ) {
- var data = event.data;
-
- // Make sure we're dealing with JSON
- if( data.charAt( 0 ) === '{' && data.charAt( data.length - 1 ) === '}' ) {
- data = JSON.parse( data );
-
- // Check if the requested method can be found
- if( data.method && typeof Reveal[data.method] === 'function' ) {
- Reveal[data.method].apply( Reveal, data.args );
- }
- }
- }, false );
- }
-
- }
-
- /**
- * Applies the configuration settings from the config
- * object. May be called multiple times.
- */
- function configure( options ) {
-
- var numberOfSlides = dom.wrapper.querySelectorAll( SLIDES_SELECTOR ).length;
-
- dom.wrapper.classList.remove( config.transition );
-
- // New config options may be passed when this method
- // is invoked through the API after initialization
- if( typeof options === 'object' ) extend( config, options );
-
- // Force linear transition based on browser capabilities
- if( features.transforms3d === false ) config.transition = 'linear';
-
- dom.wrapper.classList.add( config.transition );
-
- dom.wrapper.setAttribute( 'data-transition-speed', config.transitionSpeed );
- dom.wrapper.setAttribute( 'data-background-transition', config.backgroundTransition );
-
- dom.controls.style.display = config.controls ? 'block' : 'none';
- dom.progress.style.display = config.progress ? 'block' : 'none';
-
- if( config.rtl ) {
- dom.wrapper.classList.add( 'rtl' );
- }
- else {
- dom.wrapper.classList.remove( 'rtl' );
- }
-
- if( config.center ) {
- dom.wrapper.classList.add( 'center' );
- }
- else {
- dom.wrapper.classList.remove( 'center' );
- }
-
- // Exit the paused mode if it was configured off
- if( config.pause === false ) {
- resume();
- }
-
- if( config.mouseWheel ) {
- document.addEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF
- document.addEventListener( 'mousewheel', onDocumentMouseScroll, false );
- }
- else {
- document.removeEventListener( 'DOMMouseScroll', onDocumentMouseScroll, false ); // FF
- document.removeEventListener( 'mousewheel', onDocumentMouseScroll, false );
- }
-
- // Rolling 3D links
- if( config.rollingLinks ) {
- enableRollingLinks();
- }
- else {
- disableRollingLinks();
- }
-
- // Iframe link previews
- if( config.previewLinks ) {
- enablePreviewLinks();
- }
- else {
- disablePreviewLinks();
- enablePreviewLinks( '[data-preview-link]' );
- }
-
- // Remove existing auto-slide controls
- if( autoSlidePlayer ) {
- autoSlidePlayer.destroy();
- autoSlidePlayer = null;
- }
-
- // Generate auto-slide controls if needed
- if( numberOfSlides > 1 && config.autoSlide && config.autoSlideStoppable && features.canvas && features.requestAnimationFrame ) {
- autoSlidePlayer = new Playback( dom.wrapper, function() {
- return Math.min( Math.max( ( Date.now() - autoSlideStartTime ) / autoSlide, 0 ), 1 );
- } );
-
- autoSlidePlayer.on( 'click', onAutoSlidePlayerClick );
- autoSlidePaused = false;
- }
-
- // When fragments are turned off they should be visible
- if( config.fragments === false ) {
- toArray( dom.slides.querySelectorAll( '.fragment' ) ).forEach( function( element ) {
- element.classList.add( 'visible' );
- element.classList.remove( 'current-fragment' );
- } );
- }
-
- sync();
-
- }
-
- /**
- * Binds all event listeners.
- */
- function addEventListeners() {
-
- eventsAreBound = true;
-
- window.addEventListener( 'hashchange', onWindowHashChange, false );
- window.addEventListener( 'resize', onWindowResize, false );
-
- if( config.touch ) {
- dom.wrapper.addEventListener( 'touchstart', onTouchStart, false );
- dom.wrapper.addEventListener( 'touchmove', onTouchMove, false );
- dom.wrapper.addEventListener( 'touchend', onTouchEnd, false );
-
- // Support pointer-style touch interaction as well
- if( window.navigator.pointerEnabled ) {
- // IE 11 uses un-prefixed version of pointer events
- dom.wrapper.addEventListener( 'pointerdown', onPointerDown, false );
- dom.wrapper.addEventListener( 'pointermove', onPointerMove, false );
- dom.wrapper.addEventListener( 'pointerup', onPointerUp, false );
- }
- else if( window.navigator.msPointerEnabled ) {
- // IE 10 uses prefixed version of pointer events
- dom.wrapper.addEventListener( 'MSPointerDown', onPointerDown, false );
- dom.wrapper.addEventListener( 'MSPointerMove', onPointerMove, false );
- dom.wrapper.addEventListener( 'MSPointerUp', onPointerUp, false );
- }
- }
-
- if( config.keyboard ) {
- document.addEventListener( 'keydown', onDocumentKeyDown, false );
- document.addEventListener( 'keypress', onDocumentKeyPress, false );
- }
-
- if( config.progress && dom.progress ) {
- dom.progress.addEventListener( 'click', onProgressClicked, false );
- }
-
- if( config.focusBodyOnPageVisibilityChange ) {
- var visibilityChange;
-
- if( 'hidden' in document ) {
- visibilityChange = 'visibilitychange';
- }
- else if( 'msHidden' in document ) {
- visibilityChange = 'msvisibilitychange';
- }
- else if( 'webkitHidden' in document ) {
- visibilityChange = 'webkitvisibilitychange';
- }
-
- if( visibilityChange ) {
- document.addEventListener( visibilityChange, onPageVisibilityChange, false );
- }
- }
-
- // Listen to both touch and click events, in case the device
- // supports both
- var pointerEvents = [ 'touchstart', 'click' ];
-
- // Only support touch for Android, fixes double navigations in
- // stock browser
- if( navigator.userAgent.match( /android/gi ) ) {
- pointerEvents = [ 'touchstart' ];
- }
-
- pointerEvents.forEach( function( eventName ) {
- dom.controlsLeft.forEach( function( el ) { el.addEventListener( eventName, onNavigateLeftClicked, false ); } );
- dom.controlsRight.forEach( function( el ) { el.addEventListener( eventName, onNavigateRightClicked, false ); } );
- dom.controlsUp.forEach( function( el ) { el.addEventListener( eventName, onNavigateUpClicked, false ); } );
- dom.controlsDown.forEach( function( el ) { el.addEventListener( eventName, onNavigateDownClicked, false ); } );
- dom.controlsPrev.forEach( function( el ) { el.addEventListener( eventName, onNavigatePrevClicked, false ); } );
- dom.controlsNext.forEach( function( el ) { el.addEventListener( eventName, onNavigateNextClicked, false ); } );
- } );
-
- }
-
- /**
- * Unbinds all event listeners.
- */
- function removeEventListeners() {
-
- eventsAreBound = false;
-
- document.removeEventListener( 'keydown', onDocumentKeyDown, false );
- document.removeEventListener( 'keypress', onDocumentKeyPress, false );
- window.removeEventListener( 'hashchange', onWindowHashChange, false );
- window.removeEventListener( 'resize', onWindowResize, false );
-
- dom.wrapper.removeEventListener( 'touchstart', onTouchStart, false );
- dom.wrapper.removeEventListener( 'touchmove', onTouchMove, false );
- dom.wrapper.removeEventListener( 'touchend', onTouchEnd, false );
-
- // IE11
- if( window.navigator.pointerEnabled ) {
- dom.wrapper.removeEventListener( 'pointerdown', onPointerDown, false );
- dom.wrapper.removeEventListener( 'pointermove', onPointerMove, false );
- dom.wrapper.removeEventListener( 'pointerup', onPointerUp, false );
- }
- // IE10
- else if( window.navigator.msPointerEnabled ) {
- dom.wrapper.removeEventListener( 'MSPointerDown', onPointerDown, false );
- dom.wrapper.removeEventListener( 'MSPointerMove', onPointerMove, false );
- dom.wrapper.removeEventListener( 'MSPointerUp', onPointerUp, false );
- }
-
- if ( config.progress && dom.progress ) {
- dom.progress.removeEventListener( 'click', onProgressClicked, false );
- }
-
- [ 'touchstart', 'click' ].forEach( function( eventName ) {
- dom.controlsLeft.forEach( function( el ) { el.removeEventListener( eventName, onNavigateLeftClicked, false ); } );
- dom.controlsRight.forEach( function( el ) { el.removeEventListener( eventName, onNavigateRightClicked, false ); } );
- dom.controlsUp.forEach( function( el ) { el.removeEventListener( eventName, onNavigateUpClicked, false ); } );
- dom.controlsDown.forEach( function( el ) { el.removeEventListener( eventName, onNavigateDownClicked, false ); } );
- dom.controlsPrev.forEach( function( el ) { el.removeEventListener( eventName, onNavigatePrevClicked, false ); } );
- dom.controlsNext.forEach( function( el ) { el.removeEventListener( eventName, onNavigateNextClicked, false ); } );
- } );
-
- }
-
- /**
- * Extend object a with the properties of object b.
- * If there's a conflict, object b takes precedence.
- */
- function extend( a, b ) {
-
- for( var i in b ) {
- a[ i ] = b[ i ];
- }
-
- }
-
- /**
- * Converts the target object to an array.
- */
- function toArray( o ) {
-
- return Array.prototype.slice.call( o );
-
- }
-
- /**
- * Utility for deserializing a value.
- */
- function deserialize( value ) {
-
- if( typeof value === 'string' ) {
- if( value === 'null' ) return null;
- else if( value === 'true' ) return true;
- else if( value === 'false' ) return false;
- else if( value.match( /^\d+$/ ) ) return parseFloat( value );
- }
-
- return value;
-
- }
-
- /**
- * Measures the distance in pixels between point a
- * and point b.
- *
- * @param {Object} a point with x/y properties
- * @param {Object} b point with x/y properties
- */
- function distanceBetween( a, b ) {
-
- var dx = a.x - b.x,
- dy = a.y - b.y;
-
- return Math.sqrt( dx*dx + dy*dy );
-
- }
-
- /**
- * Applies a CSS transform to the target element.
- */
- function transformElement( element, transform ) {
-
- element.style.WebkitTransform = transform;
- element.style.MozTransform = transform;
- element.style.msTransform = transform;
- element.style.OTransform = transform;
- element.style.transform = transform;
-
- }
-
- /**
- * Injects the given CSS styles into the DOM.
- */
- function injectStyleSheet( value ) {
-
- var tag = document.createElement( 'style' );
- tag.type = 'text/css';
- if( tag.styleSheet ) {
- tag.styleSheet.cssText = value;
- }
- else {
- tag.appendChild( document.createTextNode( value ) );
- }
- document.getElementsByTagName( 'head' )[0].appendChild( tag );
-
- }
-
- /**
- * Measures the distance in pixels between point a and point b.
- *
- * @param {String} color The string representation of a color,
- * the following formats are supported:
- * - #000
- * - #000000
- * - rgb(0,0,0)
- */
- function colorToRgb( color ) {
-
- var hex3 = color.match( /^#([0-9a-f]{3})$/i );
- if( hex3 && hex3[1] ) {
- hex3 = hex3[1];
- return {
- r: parseInt( hex3.charAt( 0 ), 16 ) * 0x11,
- g: parseInt( hex3.charAt( 1 ), 16 ) * 0x11,
- b: parseInt( hex3.charAt( 2 ), 16 ) * 0x11
- };
- }
-
- var hex6 = color.match( /^#([0-9a-f]{6})$/i );
- if( hex6 && hex6[1] ) {
- hex6 = hex6[1];
- return {
- r: parseInt( hex6.substr( 0, 2 ), 16 ),
- g: parseInt( hex6.substr( 2, 2 ), 16 ),
- b: parseInt( hex6.substr( 4, 2 ), 16 )
- };
- }
-
- var rgb = color.match( /^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i );
- if( rgb ) {
- return {
- r: parseInt( rgb[1], 10 ),
- g: parseInt( rgb[2], 10 ),
- b: parseInt( rgb[3], 10 )
- };
- }
-
- var rgba = color.match( /^rgba\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\,\s*([\d]+|[\d]*.[\d]+)\s*\)$/i );
- if( rgba ) {
- return {
- r: parseInt( rgba[1], 10 ),
- g: parseInt( rgba[2], 10 ),
- b: parseInt( rgba[3], 10 ),
- a: parseFloat( rgba[4] )
- };
- }
-
- return null;
-
- }
-
- /**
- * Calculates brightness on a scale of 0-255.
- *
- * @param color See colorStringToRgb for supported formats.
- */
- function colorBrightness( color ) {
-
- if( typeof color === 'string' ) color = colorToRgb( color );
-
- if( color ) {
- return ( color.r * 299 + color.g * 587 + color.b * 114 ) / 1000;
- }
-
- return null;
-
- }
-
- /**
- * Retrieves the height of the given element by looking
- * at the position and height of its immediate children.
- */
- function getAbsoluteHeight( element ) {
-
- var height = 0;
-
- if( element ) {
- var absoluteChildren = 0;
-
- toArray( element.childNodes ).forEach( function( child ) {
-
- if( typeof child.offsetTop === 'number' && child.style ) {
- // Count # of abs children
- if( window.getComputedStyle( child ).position === 'absolute' ) {
- absoluteChildren += 1;
- }
-
- height = Math.max( height, child.offsetTop + child.offsetHeight );
- }
-
- } );
-
- // If there are no absolute children, use offsetHeight
- if( absoluteChildren === 0 ) {
- height = element.offsetHeight;
- }
-
- }
-
- return height;
-
- }
-
- /**
- * Returns the remaining height within the parent of the
- * target element.
- *
- * remaining height = [ configured parent height ] - [ current parent height ]
- */
- function getRemainingHeight( element, height ) {
-
- height = height || 0;
-
- if( element ) {
- var newHeight, oldHeight = element.style.height;
-
- // Change the .stretch element height to 0 in order find the height of all
- // the other elements
- element.style.height = '0px';
- newHeight = height - element.parentNode.offsetHeight;
-
- // Restore the old height, just in case
- element.style.height = oldHeight + 'px';
-
- return newHeight;
- }
-
- return height;
-
- }
-
- /**
- * Checks if this instance is being used to print a PDF.
- */
- function isPrintingPDF() {
-
- return ( /print-pdf/gi ).test( window.location.search );
-
- }
-
- /**
- * Hides the address bar if we're on a mobile device.
- */
- function hideAddressBar() {
-
- if( config.hideAddressBar && isMobileDevice ) {
- // Events that should trigger the address bar to hide
- window.addEventListener( 'load', removeAddressBar, false );
- window.addEventListener( 'orientationchange', removeAddressBar, false );
- }
-
- }
-
- /**
- * Causes the address bar to hide on mobile devices,
- * more vertical space ftw.
- */
- function removeAddressBar() {
-
- setTimeout( function() {
- window.scrollTo( 0, 1 );
- }, 10 );
-
- }
-
- /**
- * Dispatches an event of the specified type from the
- * reveal DOM element.
- */
- function dispatchEvent( type, args ) {
-
- var event = document.createEvent( 'HTMLEvents', 1, 2 );
- event.initEvent( type, true, true );
- extend( event, args );
- dom.wrapper.dispatchEvent( event );
-
- // If we're in an iframe, post each reveal.js event to the
- // parent window. Used by the notes plugin
- if( config.postMessageEvents && window.parent !== window.self ) {
- window.parent.postMessage( JSON.stringify({ namespace: 'reveal', eventName: type, state: getState() }), '*' );
- }
-
- }
-
- /**
- * Wrap all links in 3D goodness.
- */
- function enableRollingLinks() {
-
- if( features.transforms3d && !( 'msPerspective' in document.body.style ) ) {
- var anchors = dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ' a' );
-
- for( var i = 0, len = anchors.length; i < len; i++ ) {
- var anchor = anchors[i];
-
- if( anchor.textContent && !anchor.querySelector( '*' ) && ( !anchor.className || !anchor.classList.contains( anchor, 'roll' ) ) ) {
- var span = document.createElement('span');
- span.setAttribute('data-title', anchor.text);
- span.innerHTML = anchor.innerHTML;
-
- anchor.classList.add( 'roll' );
- anchor.innerHTML = '';
- anchor.appendChild(span);
- }
- }
- }
-
- }
-
- /**
- * Unwrap all 3D links.
- */
- function disableRollingLinks() {
-
- var anchors = dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ' a.roll' );
-
- for( var i = 0, len = anchors.length; i < len; i++ ) {
- var anchor = anchors[i];
- var span = anchor.querySelector( 'span' );
-
- if( span ) {
- anchor.classList.remove( 'roll' );
- anchor.innerHTML = span.innerHTML;
- }
- }
-
- }
-
- /**
- * Bind preview frame links.
- */
- function enablePreviewLinks( selector ) {
-
- var anchors = toArray( document.querySelectorAll( selector ? selector : 'a' ) );
-
- anchors.forEach( function( element ) {
- if( /^(http|www)/gi.test( element.getAttribute( 'href' ) ) ) {
- element.addEventListener( 'click', onPreviewLinkClicked, false );
- }
- } );
-
- }
-
- /**
- * Unbind preview frame links.
- */
- function disablePreviewLinks() {
-
- var anchors = toArray( document.querySelectorAll( 'a' ) );
-
- anchors.forEach( function( element ) {
- if( /^(http|www)/gi.test( element.getAttribute( 'href' ) ) ) {
- element.removeEventListener( 'click', onPreviewLinkClicked, false );
- }
- } );
-
- }
-
- /**
- * Opens a preview window for the target URL.
- */
- function showPreview( url ) {
-
- closeOverlay();
-
- dom.overlay = document.createElement( 'div' );
- dom.overlay.classList.add( 'overlay' );
- dom.overlay.classList.add( 'overlay-preview' );
- dom.wrapper.appendChild( dom.overlay );
-
- dom.overlay.innerHTML = [
- '',
- '
',
- '',
- '',
- '
'
- ].join('');
-
- dom.overlay.querySelector( 'iframe' ).addEventListener( 'load', function( event ) {
- dom.overlay.classList.add( 'loaded' );
- }, false );
-
- dom.overlay.querySelector( '.close' ).addEventListener( 'click', function( event ) {
- closeOverlay();
- event.preventDefault();
- }, false );
-
- dom.overlay.querySelector( '.external' ).addEventListener( 'click', function( event ) {
- closeOverlay();
- }, false );
-
- setTimeout( function() {
- dom.overlay.classList.add( 'visible' );
- }, 1 );
-
- }
-
- /**
- * Opens a overlay window with help material.
- */
- function showHelp() {
-
- if( config.help ) {
-
- closeOverlay();
-
- dom.overlay = document.createElement( 'div' );
- dom.overlay.classList.add( 'overlay' );
- dom.overlay.classList.add( 'overlay-help' );
- dom.wrapper.appendChild( dom.overlay );
-
- var html = 'Keyboard Shortcuts
';
-
- html += 'KEY ACTION ';
- for( var key in keyboardShortcuts ) {
- html += '' + key + ' ' + keyboardShortcuts[ key ] + ' ';
- }
-
- html += '
';
-
- dom.overlay.innerHTML = [
- '',
- ''
- ].join('');
-
- dom.overlay.querySelector( '.close' ).addEventListener( 'click', function( event ) {
- closeOverlay();
- event.preventDefault();
- }, false );
-
- setTimeout( function() {
- dom.overlay.classList.add( 'visible' );
- }, 1 );
-
- }
-
- }
-
- /**
- * Closes any currently open overlay.
- */
- function closeOverlay() {
-
- if( dom.overlay ) {
- dom.overlay.parentNode.removeChild( dom.overlay );
- dom.overlay = null;
- }
-
- }
-
- /**
- * Applies JavaScript-controlled layout rules to the
- * presentation.
- */
- function layout() {
-
- if( dom.wrapper && !isPrintingPDF() ) {
-
- var size = getComputedSlideSize();
-
- var slidePadding = 20; // TODO Dig this out of DOM
-
- // Layout the contents of the slides
- layoutSlideContents( config.width, config.height, slidePadding );
-
- dom.slides.style.width = size.width + 'px';
- dom.slides.style.height = size.height + 'px';
-
- // Determine scale of content to fit within available space
- scale = Math.min( size.presentationWidth / size.width, size.presentationHeight / size.height );
-
- // Respect max/min scale settings
- scale = Math.max( scale, config.minScale );
- scale = Math.min( scale, config.maxScale );
-
- // Don't apply any scaling styles if scale is 1
- if( scale === 1 ) {
- dom.slides.style.zoom = '';
- dom.slides.style.left = '';
- dom.slides.style.top = '';
- dom.slides.style.bottom = '';
- dom.slides.style.right = '';
- transformElement( dom.slides, '' );
- }
- else {
- // Prefer zooming in desktop Chrome so that content remains crisp
- if( !isMobileDevice && /chrome/i.test( navigator.userAgent ) && typeof dom.slides.style.zoom !== 'undefined' ) {
- dom.slides.style.zoom = scale;
- }
- // Apply scale transform as a fallback
- else {
- dom.slides.style.left = '50%';
- dom.slides.style.top = '50%';
- dom.slides.style.bottom = 'auto';
- dom.slides.style.right = 'auto';
- transformElement( dom.slides, 'translate(-50%, -50%) scale('+ scale +')' );
- }
- }
-
- // Select all slides, vertical and horizontal
- var slides = toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) );
-
- for( var i = 0, len = slides.length; i < len; i++ ) {
- var slide = slides[ i ];
-
- // Don't bother updating invisible slides
- if( slide.style.display === 'none' ) {
- continue;
- }
-
- if( config.center || slide.classList.contains( 'center' ) ) {
- // Vertical stacks are not centred since their section
- // children will be
- if( slide.classList.contains( 'stack' ) ) {
- slide.style.top = 0;
- }
- else {
- slide.style.top = Math.max( ( ( size.height - getAbsoluteHeight( slide ) ) / 2 ) - slidePadding, 0 ) + 'px';
- }
- }
- else {
- slide.style.top = '';
- }
-
- }
-
- updateProgress();
- updateParallax();
-
- }
-
- }
-
- /**
- * Applies layout logic to the contents of all slides in
- * the presentation.
- */
- function layoutSlideContents( width, height, padding ) {
-
- // Handle sizing of elements with the 'stretch' class
- toArray( dom.slides.querySelectorAll( 'section > .stretch' ) ).forEach( function( element ) {
-
- // Determine how much vertical space we can use
- var remainingHeight = getRemainingHeight( element, height );
-
- // Consider the aspect ratio of media elements
- if( /(img|video)/gi.test( element.nodeName ) ) {
- var nw = element.naturalWidth || element.videoWidth,
- nh = element.naturalHeight || element.videoHeight;
-
- var es = Math.min( width / nw, remainingHeight / nh );
-
- element.style.width = ( nw * es ) + 'px';
- element.style.height = ( nh * es ) + 'px';
-
- }
- else {
- element.style.width = width + 'px';
- element.style.height = remainingHeight + 'px';
- }
-
- } );
-
- }
-
- /**
- * Calculates the computed pixel size of our slides. These
- * values are based on the width and height configuration
- * options.
- */
- function getComputedSlideSize( presentationWidth, presentationHeight ) {
-
- var size = {
- // Slide size
- width: config.width,
- height: config.height,
-
- // Presentation size
- presentationWidth: presentationWidth || dom.wrapper.offsetWidth,
- presentationHeight: presentationHeight || dom.wrapper.offsetHeight
- };
-
- // Reduce available space by margin
- size.presentationWidth -= ( size.presentationHeight * config.margin );
- size.presentationHeight -= ( size.presentationHeight * config.margin );
-
- // Slide width may be a percentage of available width
- if( typeof size.width === 'string' && /%$/.test( size.width ) ) {
- size.width = parseInt( size.width, 10 ) / 100 * size.presentationWidth;
- }
-
- // Slide height may be a percentage of available height
- if( typeof size.height === 'string' && /%$/.test( size.height ) ) {
- size.height = parseInt( size.height, 10 ) / 100 * size.presentationHeight;
- }
-
- return size;
-
- }
-
- /**
- * Stores the vertical index of a stack so that the same
- * vertical slide can be selected when navigating to and
- * from the stack.
- *
- * @param {HTMLElement} stack The vertical stack element
- * @param {int} v Index to memorize
- */
- function setPreviousVerticalIndex( stack, v ) {
-
- if( typeof stack === 'object' && typeof stack.setAttribute === 'function' ) {
- stack.setAttribute( 'data-previous-indexv', v || 0 );
- }
-
- }
-
- /**
- * Retrieves the vertical index which was stored using
- * #setPreviousVerticalIndex() or 0 if no previous index
- * exists.
- *
- * @param {HTMLElement} stack The vertical stack element
- */
- function getPreviousVerticalIndex( stack ) {
-
- if( typeof stack === 'object' && typeof stack.setAttribute === 'function' && stack.classList.contains( 'stack' ) ) {
- // Prefer manually defined start-indexv
- var attributeName = stack.hasAttribute( 'data-start-indexv' ) ? 'data-start-indexv' : 'data-previous-indexv';
-
- return parseInt( stack.getAttribute( attributeName ) || 0, 10 );
- }
-
- return 0;
-
- }
-
- /**
- * Displays the overview of slides (quick nav) by
- * scaling down and arranging all slide elements.
- *
- * Experimental feature, might be dropped if perf
- * can't be improved.
- */
- function activateOverview() {
-
- // Only proceed if enabled in config
- if( config.overview ) {
-
- // Don't auto-slide while in overview mode
- cancelAutoSlide();
-
- var wasActive = dom.wrapper.classList.contains( 'overview' );
-
- // Vary the depth of the overview based on screen size
- var depth = window.innerWidth < 400 ? 1000 : 2500;
-
- dom.wrapper.classList.add( 'overview' );
- dom.wrapper.classList.remove( 'overview-deactivating' );
-
- var horizontalSlides = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR );
-
- for( var i = 0, len1 = horizontalSlides.length; i < len1; i++ ) {
- var hslide = horizontalSlides[i],
- hoffset = config.rtl ? -105 : 105;
-
- hslide.setAttribute( 'data-index-h', i );
-
- // Apply CSS transform
- transformElement( hslide, 'translateZ(-'+ depth +'px) translate(' + ( ( i - indexh ) * hoffset ) + '%, 0%)' );
-
- if( hslide.classList.contains( 'stack' ) ) {
-
- var verticalSlides = hslide.querySelectorAll( 'section' );
-
- for( var j = 0, len2 = verticalSlides.length; j < len2; j++ ) {
- var verticalIndex = i === indexh ? indexv : getPreviousVerticalIndex( hslide );
-
- var vslide = verticalSlides[j];
-
- vslide.setAttribute( 'data-index-h', i );
- vslide.setAttribute( 'data-index-v', j );
-
- // Apply CSS transform
- transformElement( vslide, 'translate(0%, ' + ( ( j - verticalIndex ) * 105 ) + '%)' );
-
- // Navigate to this slide on click
- vslide.addEventListener( 'click', onOverviewSlideClicked, true );
- }
-
- }
- else {
-
- // Navigate to this slide on click
- hslide.addEventListener( 'click', onOverviewSlideClicked, true );
-
- }
- }
-
- updateSlidesVisibility();
-
- layout();
-
- if( !wasActive ) {
- // Notify observers of the overview showing
- dispatchEvent( 'overviewshown', {
- 'indexh': indexh,
- 'indexv': indexv,
- 'currentSlide': currentSlide
- } );
- }
-
- }
-
- }
-
- /**
- * Exits the slide overview and enters the currently
- * active slide.
- */
- function deactivateOverview() {
-
- // Only proceed if enabled in config
- if( config.overview ) {
-
- dom.wrapper.classList.remove( 'overview' );
-
- // Temporarily add a class so that transitions can do different things
- // depending on whether they are exiting/entering overview, or just
- // moving from slide to slide
- dom.wrapper.classList.add( 'overview-deactivating' );
-
- setTimeout( function () {
- dom.wrapper.classList.remove( 'overview-deactivating' );
- }, 1 );
-
- // Select all slides
- toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR ) ).forEach( function( slide ) {
- // Resets all transforms to use the external styles
- transformElement( slide, '' );
-
- slide.removeEventListener( 'click', onOverviewSlideClicked, true );
- } );
-
- slide( indexh, indexv );
-
- cueAutoSlide();
-
- // Notify observers of the overview hiding
- dispatchEvent( 'overviewhidden', {
- 'indexh': indexh,
- 'indexv': indexv,
- 'currentSlide': currentSlide
- } );
-
- }
- }
-
- /**
- * Toggles the slide overview mode on and off.
- *
- * @param {Boolean} override Optional flag which overrides the
- * toggle logic and forcibly sets the desired state. True means
- * overview is open, false means it's closed.
- */
- function toggleOverview( override ) {
-
- if( typeof override === 'boolean' ) {
- override ? activateOverview() : deactivateOverview();
- }
- else {
- isOverview() ? deactivateOverview() : activateOverview();
- }
-
- }
-
- /**
- * Checks if the overview is currently active.
- *
- * @return {Boolean} true if the overview is active,
- * false otherwise
- */
- function isOverview() {
-
- return dom.wrapper.classList.contains( 'overview' );
-
- }
-
- /**
- * Checks if the current or specified slide is vertical
- * (nested within another slide).
- *
- * @param {HTMLElement} slide [optional] The slide to check
- * orientation of
- */
- function isVerticalSlide( slide ) {
-
- // Prefer slide argument, otherwise use current slide
- slide = slide ? slide : currentSlide;
-
- return slide && slide.parentNode && !!slide.parentNode.nodeName.match( /section/i );
-
- }
-
- /**
- * Handling the fullscreen functionality via the fullscreen API
- *
- * @see http://fullscreen.spec.whatwg.org/
- * @see https://developer.mozilla.org/en-US/docs/DOM/Using_fullscreen_mode
- */
- function enterFullscreen() {
-
- var element = document.body;
-
- // Check which implementation is available
- var requestMethod = element.requestFullScreen ||
- element.webkitRequestFullscreen ||
- element.webkitRequestFullScreen ||
- element.mozRequestFullScreen ||
- element.msRequestFullscreen;
-
- if( requestMethod ) {
- requestMethod.apply( element );
- }
-
- }
-
- /**
- * Enters the paused mode which fades everything on screen to
- * black.
- */
- function pause() {
-
- if( config.pause ) {
- var wasPaused = dom.wrapper.classList.contains( 'paused' );
-
- cancelAutoSlide();
- dom.wrapper.classList.add( 'paused' );
-
- if( wasPaused === false ) {
- dispatchEvent( 'paused' );
- }
- }
-
- }
-
- /**
- * Exits from the paused mode.
- */
- function resume() {
-
- var wasPaused = dom.wrapper.classList.contains( 'paused' );
- dom.wrapper.classList.remove( 'paused' );
-
- cueAutoSlide();
-
- if( wasPaused ) {
- dispatchEvent( 'resumed' );
- }
-
- }
-
- /**
- * Toggles the paused mode on and off.
- */
- function togglePause( override ) {
-
- if( typeof override === 'boolean' ) {
- override ? pause() : resume();
- }
- else {
- isPaused() ? resume() : pause();
- }
-
- }
-
- /**
- * Checks if we are currently in the paused mode.
- */
- function isPaused() {
-
- return dom.wrapper.classList.contains( 'paused' );
-
- }
-
- /**
- * Toggles the auto slide mode on and off.
- *
- * @param {Boolean} override Optional flag which sets the desired state.
- * True means autoplay starts, false means it stops.
- */
-
- function toggleAutoSlide( override ) {
-
- if( typeof override === 'boolean' ) {
- override ? resumeAutoSlide() : pauseAutoSlide();
- }
-
- else {
- autoSlidePaused ? resumeAutoSlide() : pauseAutoSlide();
- }
-
- }
-
- /**
- * Checks if the auto slide mode is currently on.
- */
- function isAutoSliding() {
-
- return !!( autoSlide && !autoSlidePaused );
-
- }
-
- /**
- * Steps from the current point in the presentation to the
- * slide which matches the specified horizontal and vertical
- * indices.
- *
- * @param {int} h Horizontal index of the target slide
- * @param {int} v Vertical index of the target slide
- * @param {int} f Optional index of a fragment within the
- * target slide to activate
- * @param {int} o Optional origin for use in multimaster environments
- */
- function slide( h, v, f, o ) {
-
- // Remember where we were at before
- previousSlide = currentSlide;
-
- // Query all horizontal slides in the deck
- var horizontalSlides = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR );
-
- // If no vertical index is specified and the upcoming slide is a
- // stack, resume at its previous vertical index
- if( v === undefined ) {
- v = getPreviousVerticalIndex( horizontalSlides[ h ] );
- }
-
- // If we were on a vertical stack, remember what vertical index
- // it was on so we can resume at the same position when returning
- if( previousSlide && previousSlide.parentNode && previousSlide.parentNode.classList.contains( 'stack' ) ) {
- setPreviousVerticalIndex( previousSlide.parentNode, indexv );
- }
-
- // Remember the state before this slide
- var stateBefore = state.concat();
-
- // Reset the state array
- state.length = 0;
-
- var indexhBefore = indexh || 0,
- indexvBefore = indexv || 0;
-
- // Activate and transition to the new slide
- indexh = updateSlides( HORIZONTAL_SLIDES_SELECTOR, h === undefined ? indexh : h );
- indexv = updateSlides( VERTICAL_SLIDES_SELECTOR, v === undefined ? indexv : v );
-
- // Update the visibility of slides now that the indices have changed
- updateSlidesVisibility();
-
- layout();
-
- // Apply the new state
- stateLoop: for( var i = 0, len = state.length; i < len; i++ ) {
- // Check if this state existed on the previous slide. If it
- // did, we will avoid adding it repeatedly
- for( var j = 0; j < stateBefore.length; j++ ) {
- if( stateBefore[j] === state[i] ) {
- stateBefore.splice( j, 1 );
- continue stateLoop;
- }
- }
-
- document.documentElement.classList.add( state[i] );
-
- // Dispatch custom event matching the state's name
- dispatchEvent( state[i] );
- }
-
- // Clean up the remains of the previous state
- while( stateBefore.length ) {
- document.documentElement.classList.remove( stateBefore.pop() );
- }
-
- // If the overview is active, re-activate it to update positions
- if( isOverview() ) {
- activateOverview();
- }
-
- // Find the current horizontal slide and any possible vertical slides
- // within it
- var currentHorizontalSlide = horizontalSlides[ indexh ],
- currentVerticalSlides = currentHorizontalSlide.querySelectorAll( 'section' );
-
- // Store references to the previous and current slides
- currentSlide = currentVerticalSlides[ indexv ] || currentHorizontalSlide;
-
- // Show fragment, if specified
- if( typeof f !== 'undefined' ) {
- navigateFragment( f );
- }
-
- // Dispatch an event if the slide changed
- var slideChanged = ( indexh !== indexhBefore || indexv !== indexvBefore );
- if( slideChanged ) {
- dispatchEvent( 'slidechanged', {
- 'indexh': indexh,
- 'indexv': indexv,
- 'previousSlide': previousSlide,
- 'currentSlide': currentSlide,
- 'origin': o
- } );
- }
- else {
- // Ensure that the previous slide is never the same as the current
- previousSlide = null;
- }
-
- // Solves an edge case where the previous slide maintains the
- // 'present' class when navigating between adjacent vertical
- // stacks
- if( previousSlide ) {
- previousSlide.classList.remove( 'present' );
- previousSlide.setAttribute( 'aria-hidden', 'true' );
-
- // Reset all slides upon navigate to home
- // Issue: #285
- if ( dom.wrapper.querySelector( HOME_SLIDE_SELECTOR ).classList.contains( 'present' ) ) {
- // Launch async task
- setTimeout( function () {
- var slides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.stack') ), i;
- for( i in slides ) {
- if( slides[i] ) {
- // Reset stack
- setPreviousVerticalIndex( slides[i], 0 );
- }
- }
- }, 0 );
- }
- }
-
- // Handle embedded content
- if( slideChanged || !previousSlide ) {
- stopEmbeddedContent( previousSlide );
- startEmbeddedContent( currentSlide );
- }
-
- // Announce the current slide contents, for screen readers
- dom.statusDiv.textContent = currentSlide.textContent;
-
- updateControls();
- updateProgress();
- updateBackground();
- updateParallax();
- updateSlideNumber();
-
- // Update the URL hash
- writeURL();
-
- cueAutoSlide();
-
- }
-
- /**
- * Syncs the presentation with the current DOM. Useful
- * when new slides or control elements are added or when
- * the configuration has changed.
- */
- function sync() {
-
- // Subscribe to input
- removeEventListeners();
- addEventListeners();
-
- // Force a layout to make sure the current config is accounted for
- layout();
-
- // Reflect the current autoSlide value
- autoSlide = config.autoSlide;
-
- // Start auto-sliding if it's enabled
- cueAutoSlide();
-
- // Re-create the slide backgrounds
- createBackgrounds();
-
- // Write the current hash to the URL
- writeURL();
-
- sortAllFragments();
-
- updateControls();
- updateProgress();
- updateBackground( true );
- updateSlideNumber();
- updateSlidesVisibility();
-
- formatEmbeddedContent();
-
- }
-
- /**
- * Resets all vertical slides so that only the first
- * is visible.
- */
- function resetVerticalSlides() {
-
- var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );
- horizontalSlides.forEach( function( horizontalSlide ) {
-
- var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) );
- verticalSlides.forEach( function( verticalSlide, y ) {
-
- if( y > 0 ) {
- verticalSlide.classList.remove( 'present' );
- verticalSlide.classList.remove( 'past' );
- verticalSlide.classList.add( 'future' );
- verticalSlide.setAttribute( 'aria-hidden', 'true' );
- }
-
- } );
-
- } );
-
- }
-
- /**
- * Sorts and formats all of fragments in the
- * presentation.
- */
- function sortAllFragments() {
-
- var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );
- horizontalSlides.forEach( function( horizontalSlide ) {
-
- var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) );
- verticalSlides.forEach( function( verticalSlide, y ) {
-
- sortFragments( verticalSlide.querySelectorAll( '.fragment' ) );
-
- } );
-
- if( verticalSlides.length === 0 ) sortFragments( horizontalSlide.querySelectorAll( '.fragment' ) );
-
- } );
-
- }
-
- /**
- * Updates one dimension of slides by showing the slide
- * with the specified index.
- *
- * @param {String} selector A CSS selector that will fetch
- * the group of slides we are working with
- * @param {Number} index The index of the slide that should be
- * shown
- *
- * @return {Number} The index of the slide that is now shown,
- * might differ from the passed in index if it was out of
- * bounds.
- */
- function updateSlides( selector, index ) {
-
- // Select all slides and convert the NodeList result to
- // an array
- var slides = toArray( dom.wrapper.querySelectorAll( selector ) ),
- slidesLength = slides.length;
-
- var printMode = isPrintingPDF();
-
- if( slidesLength ) {
-
- // Should the index loop?
- if( config.loop ) {
- index %= slidesLength;
-
- if( index < 0 ) {
- index = slidesLength + index;
- }
- }
-
- // Enforce max and minimum index bounds
- index = Math.max( Math.min( index, slidesLength - 1 ), 0 );
-
- for( var i = 0; i < slidesLength; i++ ) {
- var element = slides[i];
-
- var reverse = config.rtl && !isVerticalSlide( element );
-
- element.classList.remove( 'past' );
- element.classList.remove( 'present' );
- element.classList.remove( 'future' );
-
- // http://www.w3.org/html/wg/drafts/html/master/editing.html#the-hidden-attribute
- element.setAttribute( 'hidden', '' );
- element.setAttribute( 'aria-hidden', 'true' );
-
- // If this element contains vertical slides
- if( element.querySelector( 'section' ) ) {
- element.classList.add( 'stack' );
- }
-
- // If we're printing static slides, all slides are "present"
- if( printMode ) {
- element.classList.add( 'present' );
- continue;
- }
-
- if( i < index ) {
- // Any element previous to index is given the 'past' class
- element.classList.add( reverse ? 'future' : 'past' );
-
- if( config.fragments ) {
- var pastFragments = toArray( element.querySelectorAll( '.fragment' ) );
-
- // Show all fragments on prior slides
- while( pastFragments.length ) {
- var pastFragment = pastFragments.pop();
- pastFragment.classList.add( 'visible' );
- pastFragment.classList.remove( 'current-fragment' );
- }
- }
- }
- else if( i > index ) {
- // Any element subsequent to index is given the 'future' class
- element.classList.add( reverse ? 'past' : 'future' );
-
- if( config.fragments ) {
- var futureFragments = toArray( element.querySelectorAll( '.fragment.visible' ) );
-
- // No fragments in future slides should be visible ahead of time
- while( futureFragments.length ) {
- var futureFragment = futureFragments.pop();
- futureFragment.classList.remove( 'visible' );
- futureFragment.classList.remove( 'current-fragment' );
- }
- }
- }
- }
-
- // Mark the current slide as present
- slides[index].classList.add( 'present' );
- slides[index].removeAttribute( 'hidden' );
- slides[index].removeAttribute( 'aria-hidden' );
-
- // If this slide has a state associated with it, add it
- // onto the current state of the deck
- var slideState = slides[index].getAttribute( 'data-state' );
- if( slideState ) {
- state = state.concat( slideState.split( ' ' ) );
- }
-
- }
- else {
- // Since there are no slides we can't be anywhere beyond the
- // zeroth index
- index = 0;
- }
-
- return index;
-
- }
-
- /**
- * Optimization method; hide all slides that are far away
- * from the present slide.
- */
- function updateSlidesVisibility() {
-
- // Select all slides and convert the NodeList result to
- // an array
- var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ),
- horizontalSlidesLength = horizontalSlides.length,
- distanceX,
- distanceY;
-
- if( horizontalSlidesLength && typeof indexh !== 'undefined' ) {
-
- // The number of steps away from the present slide that will
- // be visible
- var viewDistance = isOverview() ? 10 : config.viewDistance;
-
- // Limit view distance on weaker devices
- if( isMobileDevice ) {
- viewDistance = isOverview() ? 6 : 2;
- }
-
- // Limit view distance on weaker devices
- if( isPrintingPDF() ) {
- viewDistance = Number.MAX_VALUE;
- }
-
- for( var x = 0; x < horizontalSlidesLength; x++ ) {
- var horizontalSlide = horizontalSlides[x];
-
- var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) ),
- verticalSlidesLength = verticalSlides.length;
-
- // Loops so that it measures 1 between the first and last slides
- distanceX = Math.abs( ( ( indexh || 0 ) - x ) % ( horizontalSlidesLength - viewDistance ) ) || 0;
-
- // Show the horizontal slide if it's within the view distance
- if( distanceX < viewDistance ) {
- showSlide( horizontalSlide );
- }
- else {
- hideSlide( horizontalSlide );
- }
-
- if( verticalSlidesLength ) {
-
- var oy = getPreviousVerticalIndex( horizontalSlide );
-
- for( var y = 0; y < verticalSlidesLength; y++ ) {
- var verticalSlide = verticalSlides[y];
-
- distanceY = x === ( indexh || 0 ) ? Math.abs( ( indexv || 0 ) - y ) : Math.abs( y - oy );
-
- if( distanceX + distanceY < viewDistance ) {
- showSlide( verticalSlide );
- }
- else {
- hideSlide( verticalSlide );
- }
- }
-
- }
- }
-
- }
-
- }
-
- /**
- * Updates the progress bar to reflect the current slide.
- */
- function updateProgress() {
-
- // Update progress if enabled
- if( config.progress && dom.progressbar ) {
-
- dom.progressbar.style.width = getProgress() * dom.wrapper.offsetWidth + 'px';
-
- }
-
- }
-
- /**
- * Updates the slide number div to reflect the current slide.
- */
- function updateSlideNumber() {
-
- // Update slide number if enabled
- if( config.slideNumber && dom.slideNumber) {
-
- // Display the number of the page using 'indexh - indexv' format
- var indexString = indexh;
- if( indexv > 0 ) {
- indexString += ' - ' + indexv;
- }
-
- dom.slideNumber.innerHTML = indexString;
- }
-
- }
-
- /**
- * Updates the state of all control/navigation arrows.
- */
- function updateControls() {
-
- var routes = availableRoutes();
- var fragments = availableFragments();
-
- // Remove the 'enabled' class from all directions
- dom.controlsLeft.concat( dom.controlsRight )
- .concat( dom.controlsUp )
- .concat( dom.controlsDown )
- .concat( dom.controlsPrev )
- .concat( dom.controlsNext ).forEach( function( node ) {
- node.classList.remove( 'enabled' );
- node.classList.remove( 'fragmented' );
- } );
-
- // Add the 'enabled' class to the available routes
- if( routes.left ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'enabled' ); } );
- if( routes.right ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'enabled' ); } );
- if( routes.up ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'enabled' ); } );
- if( routes.down ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'enabled' ); } );
-
- // Prev/next buttons
- if( routes.left || routes.up ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'enabled' ); } );
- if( routes.right || routes.down ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'enabled' ); } );
-
- // Highlight fragment directions
- if( currentSlide ) {
-
- // Always apply fragment decorator to prev/next buttons
- if( fragments.prev ) dom.controlsPrev.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
- if( fragments.next ) dom.controlsNext.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
-
- // Apply fragment decorators to directional buttons based on
- // what slide axis they are in
- if( isVerticalSlide( currentSlide ) ) {
- if( fragments.prev ) dom.controlsUp.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
- if( fragments.next ) dom.controlsDown.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
- }
- else {
- if( fragments.prev ) dom.controlsLeft.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
- if( fragments.next ) dom.controlsRight.forEach( function( el ) { el.classList.add( 'fragmented', 'enabled' ); } );
- }
-
- }
-
- }
-
- /**
- * Updates the background elements to reflect the current
- * slide.
- *
- * @param {Boolean} includeAll If true, the backgrounds of
- * all vertical slides (not just the present) will be updated.
- */
- function updateBackground( includeAll ) {
-
- var currentBackground = null;
-
- // Reverse past/future classes when in RTL mode
- var horizontalPast = config.rtl ? 'future' : 'past',
- horizontalFuture = config.rtl ? 'past' : 'future';
-
- // Update the classes of all backgrounds to match the
- // states of their slides (past/present/future)
- toArray( dom.background.childNodes ).forEach( function( backgroundh, h ) {
-
- backgroundh.classList.remove( 'past' );
- backgroundh.classList.remove( 'present' );
- backgroundh.classList.remove( 'future' );
-
- if( h < indexh ) {
- backgroundh.classList.add( horizontalPast );
- }
- else if ( h > indexh ) {
- backgroundh.classList.add( horizontalFuture );
- }
- else {
- backgroundh.classList.add( 'present' );
-
- // Store a reference to the current background element
- currentBackground = backgroundh;
- }
-
- if( includeAll || h === indexh ) {
- toArray( backgroundh.querySelectorAll( '.slide-background' ) ).forEach( function( backgroundv, v ) {
-
- backgroundv.classList.remove( 'past' );
- backgroundv.classList.remove( 'present' );
- backgroundv.classList.remove( 'future' );
-
- if( v < indexv ) {
- backgroundv.classList.add( 'past' );
- }
- else if ( v > indexv ) {
- backgroundv.classList.add( 'future' );
- }
- else {
- backgroundv.classList.add( 'present' );
-
- // Only if this is the present horizontal and vertical slide
- if( h === indexh ) currentBackground = backgroundv;
- }
-
- } );
- }
-
- } );
-
- // Stop any currently playing video background
- if( previousBackground ) {
-
- var previousVideo = previousBackground.querySelector( 'video' );
- if( previousVideo ) previousVideo.pause();
-
- }
-
- if( currentBackground ) {
-
- // Start video playback
- var currentVideo = currentBackground.querySelector( 'video' );
- if( currentVideo ) {
- currentVideo.currentTime = 0;
- currentVideo.play();
- }
-
- // Don't transition between identical backgrounds. This
- // prevents unwanted flicker.
- var previousBackgroundHash = previousBackground ? previousBackground.getAttribute( 'data-background-hash' ) : null;
- var currentBackgroundHash = currentBackground.getAttribute( 'data-background-hash' );
- if( currentBackgroundHash && currentBackgroundHash === previousBackgroundHash && currentBackground !== previousBackground ) {
- dom.background.classList.add( 'no-transition' );
- }
-
- previousBackground = currentBackground;
-
- }
-
- // If there's a background brightness flag for this slide,
- // bubble it to the .reveal container
- if( currentSlide ) {
- [ 'has-light-background', 'has-dark-background' ].forEach( function( classToBubble ) {
- if( currentSlide.classList.contains( classToBubble ) ) {
- dom.wrapper.classList.add( classToBubble );
- }
- else {
- dom.wrapper.classList.remove( classToBubble );
- }
- } );
- }
-
- // Allow the first background to apply without transition
- setTimeout( function() {
- dom.background.classList.remove( 'no-transition' );
- }, 1 );
-
- }
-
- /**
- * Updates the position of the parallax background based
- * on the current slide index.
- */
- function updateParallax() {
-
- if( config.parallaxBackgroundImage ) {
-
- var horizontalSlides = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ),
- verticalSlides = dom.wrapper.querySelectorAll( VERTICAL_SLIDES_SELECTOR );
-
- var backgroundSize = dom.background.style.backgroundSize.split( ' ' ),
- backgroundWidth, backgroundHeight;
-
- if( backgroundSize.length === 1 ) {
- backgroundWidth = backgroundHeight = parseInt( backgroundSize[0], 10 );
- }
- else {
- backgroundWidth = parseInt( backgroundSize[0], 10 );
- backgroundHeight = parseInt( backgroundSize[1], 10 );
- }
-
- var slideWidth = dom.background.offsetWidth;
- var horizontalSlideCount = horizontalSlides.length;
- var horizontalOffset = -( backgroundWidth - slideWidth ) / ( horizontalSlideCount-1 ) * indexh;
-
- var slideHeight = dom.background.offsetHeight;
- var verticalSlideCount = verticalSlides.length;
- var verticalOffset = verticalSlideCount > 1 ? -( backgroundHeight - slideHeight ) / ( verticalSlideCount-1 ) * indexv : 0;
-
- dom.background.style.backgroundPosition = horizontalOffset + 'px ' + verticalOffset + 'px';
-
- }
-
- }
-
- /**
- * Called when the given slide is within the configured view
- * distance. Shows the slide element and loads any content
- * that is set to load lazily (data-src).
- */
- function showSlide( slide ) {
-
- // Show the slide element
- slide.style.display = 'block';
-
- // Media elements with data-src attributes
- toArray( slide.querySelectorAll( 'img[data-src], video[data-src], audio[data-src], iframe[data-src]' ) ).forEach( function( element ) {
- element.setAttribute( 'src', element.getAttribute( 'data-src' ) );
- element.removeAttribute( 'data-src' );
- } );
-
- // Media elements with children
- toArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( media ) {
- var sources = 0;
-
- toArray( media.querySelectorAll( 'source[data-src]' ) ).forEach( function( source ) {
- source.setAttribute( 'src', source.getAttribute( 'data-src' ) );
- source.removeAttribute( 'data-src' );
- sources += 1;
- } );
-
- // If we rewrote sources for this video/audio element, we need
- // to manually tell it to load from its new origin
- if( sources > 0 ) {
- media.load();
- }
- } );
-
-
- // Show the corresponding background element
- var indices = getIndices( slide );
- var background = getSlideBackground( indices.h, indices.v );
- if( background ) {
- background.style.display = 'block';
-
- // If the background contains media, load it
- if( background.hasAttribute( 'data-loaded' ) === false ) {
- background.setAttribute( 'data-loaded', 'true' );
-
- var backgroundImage = slide.getAttribute( 'data-background-image' ),
- backgroundVideo = slide.getAttribute( 'data-background-video' ),
- backgroundIframe = slide.getAttribute( 'data-background-iframe' );
-
- // Images
- if( backgroundImage ) {
- background.style.backgroundImage = 'url('+ backgroundImage +')';
- }
- // Videos
- else if ( backgroundVideo && !isSpeakerNotes() ) {
- var video = document.createElement( 'video' );
-
- // Support comma separated lists of video sources
- backgroundVideo.split( ',' ).forEach( function( source ) {
- video.innerHTML += '';
- } );
-
- background.appendChild( video );
- }
- // Iframes
- else if ( backgroundIframe ) {
- var iframe = document.createElement( 'iframe' );
- iframe.setAttribute( 'src', backgroundIframe );
- iframe.style.width = '100%';
- iframe.style.height = '100%';
- iframe.style.maxHeight = '100%';
- iframe.style.maxWidth = '100%';
-
- background.appendChild( iframe );
- }
- }
- }
-
- }
-
- /**
- * Called when the given slide is moved outside of the
- * configured view distance.
- */
- function hideSlide( slide ) {
-
- // Hide the slide element
- slide.style.display = 'none';
-
- // Hide the corresponding background element
- var indices = getIndices( slide );
- var background = getSlideBackground( indices.h, indices.v );
- if( background ) {
- background.style.display = 'none';
- }
-
- }
-
- /**
- * Determine what available routes there are for navigation.
- *
- * @return {Object} containing four booleans: left/right/up/down
- */
- function availableRoutes() {
-
- var horizontalSlides = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ),
- verticalSlides = dom.wrapper.querySelectorAll( VERTICAL_SLIDES_SELECTOR );
-
- var routes = {
- left: indexh > 0 || config.loop,
- right: indexh < horizontalSlides.length - 1 || config.loop,
- up: indexv > 0,
- down: indexv < verticalSlides.length - 1
- };
-
- // reverse horizontal controls for rtl
- if( config.rtl ) {
- var left = routes.left;
- routes.left = routes.right;
- routes.right = left;
- }
-
- return routes;
-
- }
-
- /**
- * Returns an object describing the available fragment
- * directions.
- *
- * @return {Object} two boolean properties: prev/next
- */
- function availableFragments() {
-
- if( currentSlide && config.fragments ) {
- var fragments = currentSlide.querySelectorAll( '.fragment' );
- var hiddenFragments = currentSlide.querySelectorAll( '.fragment:not(.visible)' );
-
- return {
- prev: fragments.length - hiddenFragments.length > 0,
- next: !!hiddenFragments.length
- };
- }
- else {
- return { prev: false, next: false };
- }
-
- }
-
- /**
- * Enforces origin-specific format rules for embedded media.
- */
- function formatEmbeddedContent() {
-
- // YouTube frames must include "?enablejsapi=1"
- toArray( dom.slides.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) {
- var src = el.getAttribute( 'src' );
- if( !/enablejsapi\=1/gi.test( src ) ) {
- el.setAttribute( 'src', src + ( !/\?/.test( src ) ? '?' : '&' ) + 'enablejsapi=1' );
- }
- });
-
- // Vimeo frames must include "?api=1"
- toArray( dom.slides.querySelectorAll( 'iframe[src*="player.vimeo.com/"]' ) ).forEach( function( el ) {
- var src = el.getAttribute( 'src' );
- if( !/api\=1/gi.test( src ) ) {
- el.setAttribute( 'src', src + ( !/\?/.test( src ) ? '?' : '&' ) + 'api=1' );
- }
- });
-
- }
-
- /**
- * Start playback of any embedded content inside of
- * the targeted slide.
- */
- function startEmbeddedContent( slide ) {
-
- if( slide && !isSpeakerNotes() ) {
- // HTML5 media elements
- toArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {
- if( el.hasAttribute( 'data-autoplay' ) ) {
- el.play();
- }
- } );
-
- // iframe embeds
- toArray( slide.querySelectorAll( 'iframe' ) ).forEach( function( el ) {
- el.contentWindow.postMessage( 'slide:start', '*' );
- });
-
- // YouTube embeds
- toArray( slide.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) {
- if( el.hasAttribute( 'data-autoplay' ) ) {
- el.contentWindow.postMessage( '{"event":"command","func":"playVideo","args":""}', '*' );
- }
- });
-
- // Vimeo embeds
- toArray( slide.querySelectorAll( 'iframe[src*="player.vimeo.com/"]' ) ).forEach( function( el ) {
- if( el.hasAttribute( 'data-autoplay' ) ) {
- el.contentWindow.postMessage( '{"method":"play"}', '*' );
- }
- });
- }
-
- }
-
- /**
- * Stop playback of any embedded content inside of
- * the targeted slide.
- */
- function stopEmbeddedContent( slide ) {
-
- if( slide && slide.parentNode ) {
- // HTML5 media elements
- toArray( slide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {
- if( !el.hasAttribute( 'data-ignore' ) ) {
- el.pause();
- }
- } );
-
- // iframe embeds
- toArray( slide.querySelectorAll( 'iframe' ) ).forEach( function( el ) {
- el.contentWindow.postMessage( 'slide:stop', '*' );
- });
-
- // YouTube embeds
- toArray( slide.querySelectorAll( 'iframe[src*="youtube.com/embed/"]' ) ).forEach( function( el ) {
- if( !el.hasAttribute( 'data-ignore' ) && typeof el.contentWindow.postMessage === 'function' ) {
- el.contentWindow.postMessage( '{"event":"command","func":"pauseVideo","args":""}', '*' );
- }
- });
-
- // Vimeo embeds
- toArray( slide.querySelectorAll( 'iframe[src*="player.vimeo.com/"]' ) ).forEach( function( el ) {
- if( !el.hasAttribute( 'data-ignore' ) && typeof el.contentWindow.postMessage === 'function' ) {
- el.contentWindow.postMessage( '{"method":"pause"}', '*' );
- }
- });
- }
-
- }
-
- /**
- * Returns a value ranging from 0-1 that represents
- * how far into the presentation we have navigated.
- */
- function getProgress() {
-
- var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );
-
- // The number of past and total slides
- var totalCount = getTotalSlides();
- var pastCount = 0;
-
- // Step through all slides and count the past ones
- mainLoop: for( var i = 0; i < horizontalSlides.length; i++ ) {
-
- var horizontalSlide = horizontalSlides[i];
- var verticalSlides = toArray( horizontalSlide.querySelectorAll( 'section' ) );
-
- for( var j = 0; j < verticalSlides.length; j++ ) {
-
- // Stop as soon as we arrive at the present
- if( verticalSlides[j].classList.contains( 'present' ) ) {
- break mainLoop;
- }
-
- pastCount++;
-
- }
-
- // Stop as soon as we arrive at the present
- if( horizontalSlide.classList.contains( 'present' ) ) {
- break;
- }
-
- // Don't count the wrapping section for vertical slides
- if( horizontalSlide.classList.contains( 'stack' ) === false ) {
- pastCount++;
- }
-
- }
-
- if( currentSlide ) {
-
- var allFragments = currentSlide.querySelectorAll( '.fragment' );
-
- // If there are fragments in the current slide those should be
- // accounted for in the progress.
- if( allFragments.length > 0 ) {
- var visibleFragments = currentSlide.querySelectorAll( '.fragment.visible' );
-
- // This value represents how big a portion of the slide progress
- // that is made up by its fragments (0-1)
- var fragmentWeight = 0.9;
-
- // Add fragment progress to the past slide count
- pastCount += ( visibleFragments.length / allFragments.length ) * fragmentWeight;
- }
-
- }
-
- return pastCount / ( totalCount - 1 );
-
- }
-
- /**
- * Checks if this presentation is running inside of the
- * speaker notes window.
- */
- function isSpeakerNotes() {
-
- return !!window.location.search.match( /receiver/gi );
-
- }
-
- /**
- * Reads the current URL (hash) and navigates accordingly.
- */
- function readURL() {
-
- var hash = window.location.hash;
-
- // Attempt to parse the hash as either an index or name
- var bits = hash.slice( 2 ).split( '/' ),
- name = hash.replace( /#|\//gi, '' );
-
- // If the first bit is invalid and there is a name we can
- // assume that this is a named link
- if( isNaN( parseInt( bits[0], 10 ) ) && name.length ) {
- var element;
-
- // Ensure the named link is a valid HTML ID attribute
- if( /^[a-zA-Z][\w:.-]*$/.test( name ) ) {
- // Find the slide with the specified ID
- element = document.querySelector( '#' + name );
- }
-
- if( element ) {
- // Find the position of the named slide and navigate to it
- var indices = Reveal.getIndices( element );
- slide( indices.h, indices.v );
- }
- // If the slide doesn't exist, navigate to the current slide
- else {
- slide( indexh || 0, indexv || 0 );
- }
- }
- else {
- // Read the index components of the hash
- var h = parseInt( bits[0], 10 ) || 0,
- v = parseInt( bits[1], 10 ) || 0;
-
- if( h !== indexh || v !== indexv ) {
- slide( h, v );
- }
- }
-
- }
-
- /**
- * Updates the page URL (hash) to reflect the current
- * state.
- *
- * @param {Number} delay The time in ms to wait before
- * writing the hash
- */
- function writeURL( delay ) {
-
- if( config.history ) {
-
- // Make sure there's never more than one timeout running
- clearTimeout( writeURLTimeout );
-
- // If a delay is specified, timeout this call
- if( typeof delay === 'number' ) {
- writeURLTimeout = setTimeout( writeURL, delay );
- }
- else if( currentSlide ) {
- var url = '/';
-
- // Attempt to create a named link based on the slide's ID
- var id = currentSlide.getAttribute( 'id' );
- if( id ) {
- id = id.toLowerCase();
- id = id.replace( /[^a-zA-Z0-9\-\_\:\.]/g, '' );
- }
-
- // If the current slide has an ID, use that as a named link
- if( typeof id === 'string' && id.length ) {
- url = '/' + id;
- }
- // Otherwise use the /h/v index
- else {
- if( indexh > 0 || indexv > 0 ) url += indexh;
- if( indexv > 0 ) url += '/' + indexv;
- }
-
- window.location.hash = url;
- }
- }
-
- }
-
- /**
- * Retrieves the h/v location of the current, or specified,
- * slide.
- *
- * @param {HTMLElement} slide If specified, the returned
- * index will be for this slide rather than the currently
- * active one
- *
- * @return {Object} { h: , v: , f: }
- */
- function getIndices( slide ) {
-
- // By default, return the current indices
- var h = indexh,
- v = indexv,
- f;
-
- // If a slide is specified, return the indices of that slide
- if( slide ) {
- var isVertical = isVerticalSlide( slide );
- var slideh = isVertical ? slide.parentNode : slide;
-
- // Select all horizontal slides
- var horizontalSlides = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) );
-
- // Now that we know which the horizontal slide is, get its index
- h = Math.max( horizontalSlides.indexOf( slideh ), 0 );
-
- // Assume we're not vertical
- v = undefined;
-
- // If this is a vertical slide, grab the vertical index
- if( isVertical ) {
- v = Math.max( toArray( slide.parentNode.querySelectorAll( 'section' ) ).indexOf( slide ), 0 );
- }
- }
-
- if( !slide && currentSlide ) {
- var hasFragments = currentSlide.querySelectorAll( '.fragment' ).length > 0;
- if( hasFragments ) {
- var currentFragment = currentSlide.querySelector( '.current-fragment' );
- if( currentFragment && currentFragment.hasAttribute( 'data-fragment-index' ) ) {
- f = parseInt( currentFragment.getAttribute( 'data-fragment-index' ), 10 );
- }
- else {
- f = currentSlide.querySelectorAll( '.fragment.visible' ).length - 1;
- }
- }
- }
-
- return { h: h, v: v, f: f };
-
- }
-
- /**
- * Retrieves the total number of slides in this presentation.
- */
- function getTotalSlides() {
-
- return dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ':not(.stack)' ).length;
-
- }
-
- /**
- * Returns the slide element matching the specified index.
- */
- function getSlide( x, y ) {
-
- var horizontalSlide = dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR )[ x ];
- var verticalSlides = horizontalSlide && horizontalSlide.querySelectorAll( 'section' );
-
- if( verticalSlides && verticalSlides.length && typeof y === 'number' ) {
- return verticalSlides ? verticalSlides[ y ] : undefined;
- }
-
- return horizontalSlide;
-
- }
-
- /**
- * Returns the background element for the given slide.
- * All slides, even the ones with no background properties
- * defined, have a background element so as long as the
- * index is valid an element will be returned.
- */
- function getSlideBackground( x, y ) {
-
- // When printing to PDF the slide backgrounds are nested
- // inside of the slides
- if( isPrintingPDF() ) {
- var slide = getSlide( x, y );
- if( slide ) {
- var background = slide.querySelector( '.slide-background' );
- if( background && background.parentNode === slide ) {
- return background;
- }
- }
-
- return undefined;
- }
-
- var horizontalBackground = dom.wrapper.querySelectorAll( '.backgrounds>.slide-background' )[ x ];
- var verticalBackgrounds = horizontalBackground && horizontalBackground.querySelectorAll( '.slide-background' );
-
- if( verticalBackgrounds && verticalBackgrounds.length && typeof y === 'number' ) {
- return verticalBackgrounds ? verticalBackgrounds[ y ] : undefined;
- }
-
- return horizontalBackground;
-
- }
-
- /**
- * Retrieves the current state of the presentation as
- * an object. This state can then be restored at any
- * time.
- */
- function getState() {
-
- var indices = getIndices();
-
- return {
- indexh: indices.h,
- indexv: indices.v,
- indexf: indices.f,
- paused: isPaused(),
- overview: isOverview()
- };
-
- }
-
- /**
- * Restores the presentation to the given state.
- *
- * @param {Object} state As generated by getState()
- */
- function setState( state ) {
-
- if( typeof state === 'object' ) {
- slide( deserialize( state.indexh ), deserialize( state.indexv ), deserialize( state.indexf ) );
-
- var pausedFlag = deserialize( state.paused ),
- overviewFlag = deserialize( state.overview );
-
- if( typeof pausedFlag === 'boolean' && pausedFlag !== isPaused() ) {
- togglePause( pausedFlag );
- }
-
- if( typeof overviewFlag === 'boolean' && overviewFlag !== isOverview() ) {
- toggleOverview( overviewFlag );
- }
- }
-
- }
-
- /**
- * Return a sorted fragments list, ordered by an increasing
- * "data-fragment-index" attribute.
- *
- * Fragments will be revealed in the order that they are returned by
- * this function, so you can use the index attributes to control the
- * order of fragment appearance.
- *
- * To maintain a sensible default fragment order, fragments are presumed
- * to be passed in document order. This function adds a "fragment-index"
- * attribute to each node if such an attribute is not already present,
- * and sets that attribute to an integer value which is the position of
- * the fragment within the fragments list.
- */
- function sortFragments( fragments ) {
-
- fragments = toArray( fragments );
-
- var ordered = [],
- unordered = [],
- sorted = [];
-
- // Group ordered and unordered elements
- fragments.forEach( function( fragment, i ) {
- if( fragment.hasAttribute( 'data-fragment-index' ) ) {
- var index = parseInt( fragment.getAttribute( 'data-fragment-index' ), 10 );
-
- if( !ordered[index] ) {
- ordered[index] = [];
- }
-
- ordered[index].push( fragment );
- }
- else {
- unordered.push( [ fragment ] );
- }
- } );
-
- // Append fragments without explicit indices in their
- // DOM order
- ordered = ordered.concat( unordered );
-
- // Manually count the index up per group to ensure there
- // are no gaps
- var index = 0;
-
- // Push all fragments in their sorted order to an array,
- // this flattens the groups
- ordered.forEach( function( group ) {
- group.forEach( function( fragment ) {
- sorted.push( fragment );
- fragment.setAttribute( 'data-fragment-index', index );
- } );
-
- index ++;
- } );
-
- return sorted;
-
- }
-
- /**
- * Navigate to the specified slide fragment.
- *
- * @param {Number} index The index of the fragment that
- * should be shown, -1 means all are invisible
- * @param {Number} offset Integer offset to apply to the
- * fragment index
- *
- * @return {Boolean} true if a change was made in any
- * fragments visibility as part of this call
- */
- function navigateFragment( index, offset ) {
-
- if( currentSlide && config.fragments ) {
-
- var fragments = sortFragments( currentSlide.querySelectorAll( '.fragment' ) );
- if( fragments.length ) {
-
- // If no index is specified, find the current
- if( typeof index !== 'number' ) {
- var lastVisibleFragment = sortFragments( currentSlide.querySelectorAll( '.fragment.visible' ) ).pop();
-
- if( lastVisibleFragment ) {
- index = parseInt( lastVisibleFragment.getAttribute( 'data-fragment-index' ) || 0, 10 );
- }
- else {
- index = -1;
- }
- }
-
- // If an offset is specified, apply it to the index
- if( typeof offset === 'number' ) {
- index += offset;
- }
-
- var fragmentsShown = [],
- fragmentsHidden = [];
-
- toArray( fragments ).forEach( function( element, i ) {
-
- if( element.hasAttribute( 'data-fragment-index' ) ) {
- i = parseInt( element.getAttribute( 'data-fragment-index' ), 10 );
- }
-
- // Visible fragments
- if( i <= index ) {
- if( !element.classList.contains( 'visible' ) ) fragmentsShown.push( element );
- element.classList.add( 'visible' );
- element.classList.remove( 'current-fragment' );
-
- // Announce the fragments one by one to the Screen Reader
- dom.statusDiv.textContent = element.textContent;
-
- if( i === index ) {
- element.classList.add( 'current-fragment' );
- }
- }
- // Hidden fragments
- else {
- if( element.classList.contains( 'visible' ) ) fragmentsHidden.push( element );
- element.classList.remove( 'visible' );
- element.classList.remove( 'current-fragment' );
- }
-
-
- } );
-
- if( fragmentsHidden.length ) {
- dispatchEvent( 'fragmenthidden', { fragment: fragmentsHidden[0], fragments: fragmentsHidden } );
- }
-
- if( fragmentsShown.length ) {
- dispatchEvent( 'fragmentshown', { fragment: fragmentsShown[0], fragments: fragmentsShown } );
- }
-
- updateControls();
- updateProgress();
-
- return !!( fragmentsShown.length || fragmentsHidden.length );
-
- }
-
- }
-
- return false;
-
- }
-
- /**
- * Navigate to the next slide fragment.
- *
- * @return {Boolean} true if there was a next fragment,
- * false otherwise
- */
- function nextFragment() {
-
- return navigateFragment( null, 1 );
-
- }
-
- /**
- * Navigate to the previous slide fragment.
- *
- * @return {Boolean} true if there was a previous fragment,
- * false otherwise
- */
- function previousFragment() {
-
- return navigateFragment( null, -1 );
-
- }
-
- /**
- * Cues a new automated slide if enabled in the config.
- */
- function cueAutoSlide() {
-
- cancelAutoSlide();
-
- if( currentSlide ) {
-
- var currentFragment = currentSlide.querySelector( '.current-fragment' );
-
- var fragmentAutoSlide = currentFragment ? currentFragment.getAttribute( 'data-autoslide' ) : null;
- var parentAutoSlide = currentSlide.parentNode ? currentSlide.parentNode.getAttribute( 'data-autoslide' ) : null;
- var slideAutoSlide = currentSlide.getAttribute( 'data-autoslide' );
-
- // Pick value in the following priority order:
- // 1. Current fragment's data-autoslide
- // 2. Current slide's data-autoslide
- // 3. Parent slide's data-autoslide
- // 4. Global autoSlide setting
- if( fragmentAutoSlide ) {
- autoSlide = parseInt( fragmentAutoSlide, 10 );
- }
- else if( slideAutoSlide ) {
- autoSlide = parseInt( slideAutoSlide, 10 );
- }
- else if( parentAutoSlide ) {
- autoSlide = parseInt( parentAutoSlide, 10 );
- }
- else {
- autoSlide = config.autoSlide;
- }
-
- // If there are media elements with data-autoplay,
- // automatically set the autoSlide duration to the
- // length of that media
- toArray( currentSlide.querySelectorAll( 'video, audio' ) ).forEach( function( el ) {
- if( el.hasAttribute( 'data-autoplay' ) ) {
- if( autoSlide && el.duration * 1000 > autoSlide ) {
- autoSlide = ( el.duration * 1000 ) + 1000;
- }
- }
- } );
-
- // Cue the next auto-slide if:
- // - There is an autoSlide value
- // - Auto-sliding isn't paused by the user
- // - The presentation isn't paused
- // - The overview isn't active
- // - The presentation isn't over
- if( autoSlide && !autoSlidePaused && !isPaused() && !isOverview() && ( !Reveal.isLastSlide() || availableFragments().next || config.loop === true ) ) {
- autoSlideTimeout = setTimeout( navigateNext, autoSlide );
- autoSlideStartTime = Date.now();
- }
-
- if( autoSlidePlayer ) {
- autoSlidePlayer.setPlaying( autoSlideTimeout !== -1 );
- }
-
- }
-
- }
-
- /**
- * Cancels any ongoing request to auto-slide.
- */
- function cancelAutoSlide() {
-
- clearTimeout( autoSlideTimeout );
- autoSlideTimeout = -1;
-
- }
-
- function pauseAutoSlide() {
-
- if( autoSlide && !autoSlidePaused ) {
- autoSlidePaused = true;
- dispatchEvent( 'autoslidepaused' );
- clearTimeout( autoSlideTimeout );
-
- if( autoSlidePlayer ) {
- autoSlidePlayer.setPlaying( false );
- }
- }
-
- }
-
- function resumeAutoSlide() {
-
- if( autoSlide && autoSlidePaused ) {
- autoSlidePaused = false;
- dispatchEvent( 'autoslideresumed' );
- cueAutoSlide();
- }
-
- }
-
- function navigateLeft() {
-
- // Reverse for RTL
- if( config.rtl ) {
- if( ( isOverview() || nextFragment() === false ) && availableRoutes().left ) {
- slide( indexh + 1 );
- }
- }
- // Normal navigation
- else if( ( isOverview() || previousFragment() === false ) && availableRoutes().left ) {
- slide( indexh - 1 );
- }
-
- }
-
- function navigateRight() {
-
- // Reverse for RTL
- if( config.rtl ) {
- if( ( isOverview() || previousFragment() === false ) && availableRoutes().right ) {
- slide( indexh - 1 );
- }
- }
- // Normal navigation
- else if( ( isOverview() || nextFragment() === false ) && availableRoutes().right ) {
- slide( indexh + 1 );
- }
-
- }
-
- function navigateUp() {
-
- // Prioritize hiding fragments
- if( ( isOverview() || previousFragment() === false ) && availableRoutes().up ) {
- slide( indexh, indexv - 1 );
- }
-
- }
-
- function navigateDown() {
-
- // Prioritize revealing fragments
- if( ( isOverview() || nextFragment() === false ) && availableRoutes().down ) {
- slide( indexh, indexv + 1 );
- }
-
- }
-
- /**
- * Navigates backwards, prioritized in the following order:
- * 1) Previous fragment
- * 2) Previous vertical slide
- * 3) Previous horizontal slide
- */
- function navigatePrev() {
-
- // Prioritize revealing fragments
- if( previousFragment() === false ) {
- if( availableRoutes().up ) {
- navigateUp();
- }
- else {
- // Fetch the previous horizontal slide, if there is one
- var previousSlide;
-
- if( config.rtl ) {
- previousSlide = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.future' ) ).pop();
- }
- else {
- previousSlide = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR + '.past' ) ).pop();
- }
-
- if( previousSlide ) {
- var v = ( previousSlide.querySelectorAll( 'section' ).length - 1 ) || undefined;
- var h = indexh - 1;
- slide( h, v );
- }
- }
- }
-
- }
-
- /**
- * The reverse of #navigatePrev().
- */
- function navigateNext() {
-
- // Prioritize revealing fragments
- if( nextFragment() === false ) {
- if( availableRoutes().down ) {
- navigateDown();
- }
- else if( config.rtl ) {
- navigateLeft();
- }
- else {
- navigateRight();
- }
- }
-
- // If auto-sliding is enabled we need to cue up
- // another timeout
- cueAutoSlide();
-
- }
-
-
- // --------------------------------------------------------------------//
- // ----------------------------- EVENTS -------------------------------//
- // --------------------------------------------------------------------//
-
- /**
- * Called by all event handlers that are based on user
- * input.
- */
- function onUserInput( event ) {
-
- if( config.autoSlideStoppable ) {
- pauseAutoSlide();
- }
-
- }
-
- /**
- * Handler for the document level 'keypress' event.
- */
- function onDocumentKeyPress( event ) {
-
- // Check if the pressed key is question mark
- if( event.shiftKey && event.charCode === 63 ) {
- if( dom.overlay ) {
- closeOverlay();
- }
- else {
- showHelp( true );
- }
- }
-
- }
-
- /**
- * Handler for the document level 'keydown' event.
- */
- function onDocumentKeyDown( event ) {
-
- // If there's a condition specified and it returns false,
- // ignore this event
- if( typeof config.keyboardCondition === 'function' && config.keyboardCondition() === false ) {
- return true;
- }
-
- // Remember if auto-sliding was paused so we can toggle it
- var autoSlideWasPaused = autoSlidePaused;
-
- onUserInput( event );
-
- // Check if there's a focused element that could be using
- // the keyboard
- var activeElementIsCE = document.activeElement && document.activeElement.contentEditable !== 'inherit';
- var activeElementIsInput = document.activeElement && document.activeElement.tagName && /input|textarea/i.test( document.activeElement.tagName );
-
- // Disregard the event if there's a focused element or a
- // keyboard modifier key is present
- if( activeElementIsCE || activeElementIsInput || (event.shiftKey && event.keyCode !== 32) || event.altKey || event.ctrlKey || event.metaKey ) return;
-
- // While paused only allow "unpausing" keyboard events (b and .)
- if( isPaused() && [66,190,191].indexOf( event.keyCode ) === -1 ) {
- return false;
- }
-
- var triggered = false;
-
- // 1. User defined key bindings
- if( typeof config.keyboard === 'object' ) {
-
- for( var key in config.keyboard ) {
-
- // Check if this binding matches the pressed key
- if( parseInt( key, 10 ) === event.keyCode ) {
-
- var value = config.keyboard[ key ];
-
- // Callback function
- if( typeof value === 'function' ) {
- value.apply( null, [ event ] );
- }
- // String shortcuts to reveal.js API
- else if( typeof value === 'string' && typeof Reveal[ value ] === 'function' ) {
- Reveal[ value ].call();
- }
-
- triggered = true;
-
- }
-
- }
-
- }
-
- // 2. System defined key bindings
- if( triggered === false ) {
-
- // Assume true and try to prove false
- triggered = true;
-
- switch( event.keyCode ) {
- // p, page up
- case 80: case 33: navigatePrev(); break;
- // n, page down
- case 78: case 34: navigateNext(); break;
- // h, left
- case 72: case 37: navigateLeft(); break;
- // l, right
- case 76: case 39: navigateRight(); break;
- // k, up
- case 75: case 38: navigateUp(); break;
- // j, down
- case 74: case 40: navigateDown(); break;
- // home
- case 36: slide( 0 ); break;
- // end
- case 35: slide( Number.MAX_VALUE ); break;
- // space
- case 32: isOverview() ? deactivateOverview() : event.shiftKey ? navigatePrev() : navigateNext(); break;
- // return
- case 13: isOverview() ? deactivateOverview() : triggered = false; break;
- // two-spot, semicolon, b, period, Logitech presenter tools "black screen" button
- case 58: case 59: case 66: case 190: case 191: togglePause(); break;
- // f
- case 70: enterFullscreen(); break;
- // a
- case 65: if ( config.autoSlideStoppable ) toggleAutoSlide( autoSlideWasPaused ); break;
- default:
- triggered = false;
- }
-
- }
-
- // If the input resulted in a triggered action we should prevent
- // the browsers default behavior
- if( triggered ) {
- event.preventDefault && event.preventDefault();
- }
- // ESC or O key
- else if ( ( event.keyCode === 27 || event.keyCode === 79 ) && features.transforms3d ) {
- if( dom.overlay ) {
- closeOverlay();
- }
- else {
- toggleOverview();
- }
-
- event.preventDefault && event.preventDefault();
- }
-
- // If auto-sliding is enabled we need to cue up
- // another timeout
- cueAutoSlide();
-
- }
-
- /**
- * Handler for the 'touchstart' event, enables support for
- * swipe and pinch gestures.
- */
- function onTouchStart( event ) {
-
- touch.startX = event.touches[0].clientX;
- touch.startY = event.touches[0].clientY;
- touch.startCount = event.touches.length;
-
- // If there's two touches we need to memorize the distance
- // between those two points to detect pinching
- if( event.touches.length === 2 && config.overview ) {
- touch.startSpan = distanceBetween( {
- x: event.touches[1].clientX,
- y: event.touches[1].clientY
- }, {
- x: touch.startX,
- y: touch.startY
- } );
- }
-
- }
-
- /**
- * Handler for the 'touchmove' event.
- */
- function onTouchMove( event ) {
-
- // Each touch should only trigger one action
- if( !touch.captured ) {
- onUserInput( event );
-
- var currentX = event.touches[0].clientX;
- var currentY = event.touches[0].clientY;
-
- // If the touch started with two points and still has
- // two active touches; test for the pinch gesture
- if( event.touches.length === 2 && touch.startCount === 2 && config.overview ) {
-
- // The current distance in pixels between the two touch points
- var currentSpan = distanceBetween( {
- x: event.touches[1].clientX,
- y: event.touches[1].clientY
- }, {
- x: touch.startX,
- y: touch.startY
- } );
-
- // If the span is larger than the desire amount we've got
- // ourselves a pinch
- if( Math.abs( touch.startSpan - currentSpan ) > touch.threshold ) {
- touch.captured = true;
-
- if( currentSpan < touch.startSpan ) {
- activateOverview();
- }
- else {
- deactivateOverview();
- }
- }
-
- event.preventDefault();
-
- }
- // There was only one touch point, look for a swipe
- else if( event.touches.length === 1 && touch.startCount !== 2 ) {
-
- var deltaX = currentX - touch.startX,
- deltaY = currentY - touch.startY;
-
- if( deltaX > touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
- touch.captured = true;
- navigateLeft();
- }
- else if( deltaX < -touch.threshold && Math.abs( deltaX ) > Math.abs( deltaY ) ) {
- touch.captured = true;
- navigateRight();
- }
- else if( deltaY > touch.threshold ) {
- touch.captured = true;
- navigateUp();
- }
- else if( deltaY < -touch.threshold ) {
- touch.captured = true;
- navigateDown();
- }
-
- // If we're embedded, only block touch events if they have
- // triggered an action
- if( config.embedded ) {
- if( touch.captured || isVerticalSlide( currentSlide ) ) {
- event.preventDefault();
- }
- }
- // Not embedded? Block them all to avoid needless tossing
- // around of the viewport in iOS
- else {
- event.preventDefault();
- }
-
- }
- }
- // There's a bug with swiping on some Android devices unless
- // the default action is always prevented
- else if( navigator.userAgent.match( /android/gi ) ) {
- event.preventDefault();
- }
-
- }
-
- /**
- * Handler for the 'touchend' event.
- */
- function onTouchEnd( event ) {
-
- touch.captured = false;
-
- }
-
- /**
- * Convert pointer down to touch start.
- */
- function onPointerDown( event ) {
-
- if( event.pointerType === event.MSPOINTER_TYPE_TOUCH || event.pointerType === "touch" ) {
- event.touches = [{ clientX: event.clientX, clientY: event.clientY }];
- onTouchStart( event );
- }
-
- }
-
- /**
- * Convert pointer move to touch move.
- */
- function onPointerMove( event ) {
-
- if( event.pointerType === event.MSPOINTER_TYPE_TOUCH || event.pointerType === "touch" ) {
- event.touches = [{ clientX: event.clientX, clientY: event.clientY }];
- onTouchMove( event );
- }
-
- }
-
- /**
- * Convert pointer up to touch end.
- */
- function onPointerUp( event ) {
-
- if( event.pointerType === event.MSPOINTER_TYPE_TOUCH || event.pointerType === "touch" ) {
- event.touches = [{ clientX: event.clientX, clientY: event.clientY }];
- onTouchEnd( event );
- }
-
- }
-
- /**
- * Handles mouse wheel scrolling, throttled to avoid skipping
- * multiple slides.
- */
- function onDocumentMouseScroll( event ) {
-
- if( Date.now() - lastMouseWheelStep > 600 ) {
-
- lastMouseWheelStep = Date.now();
-
- var delta = event.detail || -event.wheelDelta;
- if( delta > 0 ) {
- navigateNext();
- }
- else {
- navigatePrev();
- }
-
- }
-
- }
-
- /**
- * Clicking on the progress bar results in a navigation to the
- * closest approximate horizontal slide using this equation:
- *
- * ( clickX / presentationWidth ) * numberOfSlides
- */
- function onProgressClicked( event ) {
-
- onUserInput( event );
-
- event.preventDefault();
-
- var slidesTotal = toArray( dom.wrapper.querySelectorAll( HORIZONTAL_SLIDES_SELECTOR ) ).length;
- var slideIndex = Math.floor( ( event.clientX / dom.wrapper.offsetWidth ) * slidesTotal );
-
- slide( slideIndex );
-
- }
-
- /**
- * Event handler for navigation control buttons.
- */
- function onNavigateLeftClicked( event ) { event.preventDefault(); onUserInput(); navigateLeft(); }
- function onNavigateRightClicked( event ) { event.preventDefault(); onUserInput(); navigateRight(); }
- function onNavigateUpClicked( event ) { event.preventDefault(); onUserInput(); navigateUp(); }
- function onNavigateDownClicked( event ) { event.preventDefault(); onUserInput(); navigateDown(); }
- function onNavigatePrevClicked( event ) { event.preventDefault(); onUserInput(); navigatePrev(); }
- function onNavigateNextClicked( event ) { event.preventDefault(); onUserInput(); navigateNext(); }
-
- /**
- * Handler for the window level 'hashchange' event.
- */
- function onWindowHashChange( event ) {
-
- readURL();
-
- }
-
- /**
- * Handler for the window level 'resize' event.
- */
- function onWindowResize( event ) {
-
- layout();
-
- }
-
- /**
- * Handle for the window level 'visibilitychange' event.
- */
- function onPageVisibilityChange( event ) {
-
- var isHidden = document.webkitHidden ||
- document.msHidden ||
- document.hidden;
-
- // If, after clicking a link or similar and we're coming back,
- // focus the document.body to ensure we can use keyboard shortcuts
- if( isHidden === false && document.activeElement !== document.body ) {
- document.activeElement.blur();
- document.body.focus();
- }
-
- }
-
- /**
- * Invoked when a slide is and we're in the overview.
- */
- function onOverviewSlideClicked( event ) {
-
- // TODO There's a bug here where the event listeners are not
- // removed after deactivating the overview.
- if( eventsAreBound && isOverview() ) {
- event.preventDefault();
-
- var element = event.target;
-
- while( element && !element.nodeName.match( /section/gi ) ) {
- element = element.parentNode;
- }
-
- if( element && !element.classList.contains( 'disabled' ) ) {
-
- deactivateOverview();
-
- if( element.nodeName.match( /section/gi ) ) {
- var h = parseInt( element.getAttribute( 'data-index-h' ), 10 ),
- v = parseInt( element.getAttribute( 'data-index-v' ), 10 );
-
- slide( h, v );
- }
-
- }
- }
-
- }
-
- /**
- * Handles clicks on links that are set to preview in the
- * iframe overlay.
- */
- function onPreviewLinkClicked( event ) {
-
- if( event.currentTarget && event.currentTarget.hasAttribute( 'href' ) ) {
- var url = event.currentTarget.getAttribute( 'href' );
- if( url ) {
- showPreview( url );
- event.preventDefault();
- }
- }
-
- }
-
- /**
- * Handles click on the auto-sliding controls element.
- */
- function onAutoSlidePlayerClick( event ) {
-
- // Replay
- if( Reveal.isLastSlide() && config.loop === false ) {
- slide( 0, 0 );
- resumeAutoSlide();
- }
- // Resume
- else if( autoSlidePaused ) {
- resumeAutoSlide();
- }
- // Pause
- else {
- pauseAutoSlide();
- }
-
- }
-
-
- // --------------------------------------------------------------------//
- // ------------------------ PLAYBACK COMPONENT ------------------------//
- // --------------------------------------------------------------------//
-
-
- /**
- * Constructor for the playback component, which displays
- * play/pause/progress controls.
- *
- * @param {HTMLElement} container The component will append
- * itself to this
- * @param {Function} progressCheck A method which will be
- * called frequently to get the current progress on a range
- * of 0-1
- */
- function Playback( container, progressCheck ) {
-
- // Cosmetics
- this.diameter = 50;
- this.thickness = 3;
-
- // Flags if we are currently playing
- this.playing = false;
-
- // Current progress on a 0-1 range
- this.progress = 0;
-
- // Used to loop the animation smoothly
- this.progressOffset = 1;
-
- this.container = container;
- this.progressCheck = progressCheck;
-
- this.canvas = document.createElement( 'canvas' );
- this.canvas.className = 'playback';
- this.canvas.width = this.diameter;
- this.canvas.height = this.diameter;
- this.context = this.canvas.getContext( '2d' );
-
- this.container.appendChild( this.canvas );
-
- this.render();
-
- }
-
- Playback.prototype.setPlaying = function( value ) {
-
- var wasPlaying = this.playing;
-
- this.playing = value;
-
- // Start repainting if we weren't already
- if( !wasPlaying && this.playing ) {
- this.animate();
- }
- else {
- this.render();
- }
-
- };
-
- Playback.prototype.animate = function() {
-
- var progressBefore = this.progress;
-
- this.progress = this.progressCheck();
-
- // When we loop, offset the progress so that it eases
- // smoothly rather than immediately resetting
- if( progressBefore > 0.8 && this.progress < 0.2 ) {
- this.progressOffset = this.progress;
- }
-
- this.render();
-
- if( this.playing ) {
- features.requestAnimationFrameMethod.call( window, this.animate.bind( this ) );
- }
-
- };
-
- /**
- * Renders the current progress and playback state.
- */
- Playback.prototype.render = function() {
-
- var progress = this.playing ? this.progress : 0,
- radius = ( this.diameter / 2 ) - this.thickness,
- x = this.diameter / 2,
- y = this.diameter / 2,
- iconSize = 14;
-
- // Ease towards 1
- this.progressOffset += ( 1 - this.progressOffset ) * 0.1;
-
- var endAngle = ( - Math.PI / 2 ) + ( progress * ( Math.PI * 2 ) );
- var startAngle = ( - Math.PI / 2 ) + ( this.progressOffset * ( Math.PI * 2 ) );
-
- this.context.save();
- this.context.clearRect( 0, 0, this.diameter, this.diameter );
-
- // Solid background color
- this.context.beginPath();
- this.context.arc( x, y, radius + 2, 0, Math.PI * 2, false );
- this.context.fillStyle = 'rgba( 0, 0, 0, 0.4 )';
- this.context.fill();
-
- // Draw progress track
- this.context.beginPath();
- this.context.arc( x, y, radius, 0, Math.PI * 2, false );
- this.context.lineWidth = this.thickness;
- this.context.strokeStyle = '#666';
- this.context.stroke();
-
- if( this.playing ) {
- // Draw progress on top of track
- this.context.beginPath();
- this.context.arc( x, y, radius, startAngle, endAngle, false );
- this.context.lineWidth = this.thickness;
- this.context.strokeStyle = '#fff';
- this.context.stroke();
- }
-
- this.context.translate( x - ( iconSize / 2 ), y - ( iconSize / 2 ) );
-
- // Draw play/pause icons
- if( this.playing ) {
- this.context.fillStyle = '#fff';
- this.context.fillRect( 0, 0, iconSize / 2 - 2, iconSize );
- this.context.fillRect( iconSize / 2 + 2, 0, iconSize / 2 - 2, iconSize );
- }
- else {
- this.context.beginPath();
- this.context.translate( 2, 0 );
- this.context.moveTo( 0, 0 );
- this.context.lineTo( iconSize - 2, iconSize / 2 );
- this.context.lineTo( 0, iconSize );
- this.context.fillStyle = '#fff';
- this.context.fill();
- }
-
- this.context.restore();
-
- };
-
- Playback.prototype.on = function( type, listener ) {
- this.canvas.addEventListener( type, listener, false );
- };
-
- Playback.prototype.off = function( type, listener ) {
- this.canvas.removeEventListener( type, listener, false );
- };
-
- Playback.prototype.destroy = function() {
-
- this.playing = false;
-
- if( this.canvas.parentNode ) {
- this.container.removeChild( this.canvas );
- }
-
- };
-
-
- // --------------------------------------------------------------------//
- // ------------------------------- API --------------------------------//
- // --------------------------------------------------------------------//
-
-
- Reveal = {
- initialize: initialize,
- configure: configure,
- sync: sync,
-
- // Navigation methods
- slide: slide,
- left: navigateLeft,
- right: navigateRight,
- up: navigateUp,
- down: navigateDown,
- prev: navigatePrev,
- next: navigateNext,
-
- // Fragment methods
- navigateFragment: navigateFragment,
- prevFragment: previousFragment,
- nextFragment: nextFragment,
-
- // Deprecated aliases
- navigateTo: slide,
- navigateLeft: navigateLeft,
- navigateRight: navigateRight,
- navigateUp: navigateUp,
- navigateDown: navigateDown,
- navigatePrev: navigatePrev,
- navigateNext: navigateNext,
-
- // Forces an update in slide layout
- layout: layout,
-
- // Returns an object with the available routes as booleans (left/right/top/bottom)
- availableRoutes: availableRoutes,
-
- // Returns an object with the available fragments as booleans (prev/next)
- availableFragments: availableFragments,
-
- // Toggles the overview mode on/off
- toggleOverview: toggleOverview,
-
- // Toggles the "black screen" mode on/off
- togglePause: togglePause,
-
- // Toggles the auto slide mode on/off
- toggleAutoSlide: toggleAutoSlide,
-
- // State checks
- isOverview: isOverview,
- isPaused: isPaused,
- isAutoSliding: isAutoSliding,
-
- // Adds or removes all internal event listeners (such as keyboard)
- addEventListeners: addEventListeners,
- removeEventListeners: removeEventListeners,
-
- // Facility for persisting and restoring the presentation state
- getState: getState,
- setState: setState,
-
- // Presentation progress on range of 0-1
- getProgress: getProgress,
-
- // Returns the indices of the current, or specified, slide
- getIndices: getIndices,
-
- getTotalSlides: getTotalSlides,
-
- // Returns the slide element at the specified index
- getSlide: getSlide,
-
- // Returns the slide background element at the specified index
- getSlideBackground: getSlideBackground,
-
- // Returns the previous slide element, may be null
- getPreviousSlide: function() {
- return previousSlide;
- },
-
- // Returns the current slide element
- getCurrentSlide: function() {
- return currentSlide;
- },
-
- // Returns the current scale of the presentation content
- getScale: function() {
- return scale;
- },
-
- // Returns the current configuration object
- getConfig: function() {
- return config;
- },
-
- // Helper method, retrieves query string as a key/value hash
- getQueryHash: function() {
- var query = {};
-
- location.search.replace( /[A-Z0-9]+?=([\w\.%-]*)/gi, function(a) {
- query[ a.split( '=' ).shift() ] = a.split( '=' ).pop();
- } );
-
- // Basic deserialization
- for( var i in query ) {
- var value = query[ i ];
-
- query[ i ] = deserialize( unescape( value ) );
- }
-
- return query;
- },
-
- // Returns true if we're currently on the first slide
- isFirstSlide: function() {
- return ( indexh === 0 && indexv === 0 );
- },
-
- // Returns true if we're currently on the last slide
- isLastSlide: function() {
- if( currentSlide ) {
- // Does this slide has next a sibling?
- if( currentSlide.nextElementSibling ) return false;
-
- // If it's vertical, does its parent have a next sibling?
- if( isVerticalSlide( currentSlide ) && currentSlide.parentNode.nextElementSibling ) return false;
-
- return true;
- }
-
- return false;
- },
-
- // Checks if reveal.js has been loaded and is ready for use
- isReady: function() {
- return loaded;
- },
-
- // Forward event binding to the reveal DOM element
- addEventListener: function( type, listener, useCapture ) {
- if( 'addEventListener' in window ) {
- ( dom.wrapper || document.querySelector( '.reveal' ) ).addEventListener( type, listener, useCapture );
- }
- },
- removeEventListener: function( type, listener, useCapture ) {
- if( 'addEventListener' in window ) {
- ( dom.wrapper || document.querySelector( '.reveal' ) ).removeEventListener( type, listener, useCapture );
- }
- },
-
- // Programatically triggers a keyboard event
- triggerKey: function( keyCode ) {
- onDocumentKeyDown( { keyCode: keyCode } );
- }
- };
-
- return Reveal;
-
-}));
diff --git a/presentation/lib/css/zenburn.css b/presentation/lib/css/zenburn.css
deleted file mode 100644
index f6cb0983a..000000000
--- a/presentation/lib/css/zenburn.css
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
-
-Zenburn style from voldmar.ru (c) Vladimir Epifanov
-based on dark.css by Ivan Sagalaev
-
-*/
-
-.hljs {
- display: block; padding: 0.5em;
- background: #3F3F3F;
- color: #DCDCDC;
-}
-
-.hljs-keyword,
-.hljs-tag,
-.css .hljs-class,
-.css .hljs-id,
-.lisp .hljs-title,
-.nginx .hljs-title,
-.hljs-request,
-.hljs-status,
-.clojure .hljs-attribute {
- color: #E3CEAB;
-}
-
-.django .hljs-template_tag,
-.django .hljs-variable,
-.django .hljs-filter .hljs-argument {
- color: #DCDCDC;
-}
-
-.hljs-number,
-.hljs-date {
- color: #8CD0D3;
-}
-
-.dos .hljs-envvar,
-.dos .hljs-stream,
-.hljs-variable,
-.apache .hljs-sqbracket {
- color: #EFDCBC;
-}
-
-.dos .hljs-flow,
-.diff .hljs-change,
-.python .exception,
-.python .hljs-built_in,
-.hljs-literal,
-.tex .hljs-special {
- color: #EFEFAF;
-}
-
-.diff .hljs-chunk,
-.hljs-subst {
- color: #8F8F8F;
-}
-
-.dos .hljs-keyword,
-.python .hljs-decorator,
-.hljs-title,
-.haskell .hljs-type,
-.diff .hljs-header,
-.ruby .hljs-class .hljs-parent,
-.apache .hljs-tag,
-.nginx .hljs-built_in,
-.tex .hljs-command,
-.hljs-prompt {
- color: #efef8f;
-}
-
-.dos .hljs-winutils,
-.ruby .hljs-symbol,
-.ruby .hljs-symbol .hljs-string,
-.ruby .hljs-string {
- color: #DCA3A3;
-}
-
-.diff .hljs-deletion,
-.hljs-string,
-.hljs-tag .hljs-value,
-.hljs-preprocessor,
-.hljs-pragma,
-.hljs-built_in,
-.sql .hljs-aggregate,
-.hljs-javadoc,
-.smalltalk .hljs-class,
-.smalltalk .hljs-localvars,
-.smalltalk .hljs-array,
-.css .hljs-rules .hljs-value,
-.hljs-attr_selector,
-.hljs-pseudo,
-.apache .hljs-cbracket,
-.tex .hljs-formula,
-.coffeescript .hljs-attribute {
- color: #CC9393;
-}
-
-.hljs-shebang,
-.diff .hljs-addition,
-.hljs-comment,
-.java .hljs-annotation,
-.hljs-template_comment,
-.hljs-pi,
-.hljs-doctype {
- color: #7F9F7F;
-}
-
-.coffeescript .javascript,
-.javascript .xml,
-.tex .hljs-formula,
-.xml .javascript,
-.xml .vbscript,
-.xml .css,
-.xml .hljs-cdata {
- opacity: 0.5;
-}
-
diff --git a/presentation/lib/font/league-gothic/LICENSE b/presentation/lib/font/league-gothic/LICENSE
deleted file mode 100644
index 29513e9c4..000000000
--- a/presentation/lib/font/league-gothic/LICENSE
+++ /dev/null
@@ -1,2 +0,0 @@
-SIL Open Font License (OFL)
-http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL
diff --git a/presentation/lib/font/league-gothic/league-gothic.css b/presentation/lib/font/league-gothic/league-gothic.css
deleted file mode 100644
index 44a33a14e..000000000
--- a/presentation/lib/font/league-gothic/league-gothic.css
+++ /dev/null
@@ -1,10 +0,0 @@
-@font-face {
- font-family: 'League Gothic';
- src: url('league-gothic.eot');
- src: url('league-gothic.eot?#iefix') format('embedded-opentype'),
- url('league-gothic.woff') format('woff'),
- url('league-gothic.ttf') format('truetype');
-
- font-weight: normal;
- font-style: normal;
-}
\ No newline at end of file
diff --git a/presentation/lib/font/league-gothic/league-gothic.eot b/presentation/lib/font/league-gothic/league-gothic.eot
deleted file mode 100755
index f62619aef..000000000
Binary files a/presentation/lib/font/league-gothic/league-gothic.eot and /dev/null differ
diff --git a/presentation/lib/font/league-gothic/league-gothic.ttf b/presentation/lib/font/league-gothic/league-gothic.ttf
deleted file mode 100755
index baa9a9500..000000000
Binary files a/presentation/lib/font/league-gothic/league-gothic.ttf and /dev/null differ
diff --git a/presentation/lib/font/league-gothic/league-gothic.woff b/presentation/lib/font/league-gothic/league-gothic.woff
deleted file mode 100755
index 8c1227b20..000000000
Binary files a/presentation/lib/font/league-gothic/league-gothic.woff and /dev/null differ
diff --git a/presentation/lib/font/source-sans-pro/LICENSE b/presentation/lib/font/source-sans-pro/LICENSE
deleted file mode 100644
index 71b7a02a2..000000000
--- a/presentation/lib/font/source-sans-pro/LICENSE
+++ /dev/null
@@ -1,45 +0,0 @@
-SIL Open Font License
-
-Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name ‘Source’. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
-
-This Font Software is licensed under the SIL Open Font License, Version 1.1.
-This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
-
-—————————————————————————————-
-SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-—————————————————————————————-
-
-PREAMBLE
-The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
-
-The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
-
-DEFINITIONS
-“Font Software” refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
-
-“Reserved Font Name” refers to any names specified as such after the copyright statement(s).
-
-“Original Version” refers to the collection of Font Software components as distributed by the Copyright Holder(s).
-
-“Modified Version” refers to any derivative made by adding to, deleting, or substituting—in part or in whole—any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
-
-“Author” refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
-
-PERMISSION & CONDITIONS
-Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
-
-1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
-
-2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
-
-3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
-
-4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
-
-5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
-
-TERMINATION
-This license becomes null and void if any of the above conditions are not met.
-
-DISCLAIMER
-THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.
\ No newline at end of file
diff --git a/presentation/lib/font/source-sans-pro/source-sans-pro-italic.eot b/presentation/lib/font/source-sans-pro/source-sans-pro-italic.eot
deleted file mode 100755
index 32fe466bb..000000000
Binary files a/presentation/lib/font/source-sans-pro/source-sans-pro-italic.eot and /dev/null differ
diff --git a/presentation/lib/font/source-sans-pro/source-sans-pro-italic.ttf b/presentation/lib/font/source-sans-pro/source-sans-pro-italic.ttf
deleted file mode 100755
index f9ac13ffc..000000000
Binary files a/presentation/lib/font/source-sans-pro/source-sans-pro-italic.ttf and /dev/null differ
diff --git a/presentation/lib/font/source-sans-pro/source-sans-pro-italic.woff b/presentation/lib/font/source-sans-pro/source-sans-pro-italic.woff
deleted file mode 100755
index ceecbf17f..000000000
Binary files a/presentation/lib/font/source-sans-pro/source-sans-pro-italic.woff and /dev/null differ
diff --git a/presentation/lib/font/source-sans-pro/source-sans-pro-regular.eot b/presentation/lib/font/source-sans-pro/source-sans-pro-regular.eot
deleted file mode 100755
index 4d29ddadd..000000000
Binary files a/presentation/lib/font/source-sans-pro/source-sans-pro-regular.eot and /dev/null differ
diff --git a/presentation/lib/font/source-sans-pro/source-sans-pro-regular.ttf b/presentation/lib/font/source-sans-pro/source-sans-pro-regular.ttf
deleted file mode 100755
index 00c833cdc..000000000
Binary files a/presentation/lib/font/source-sans-pro/source-sans-pro-regular.ttf and /dev/null differ
diff --git a/presentation/lib/font/source-sans-pro/source-sans-pro-regular.woff b/presentation/lib/font/source-sans-pro/source-sans-pro-regular.woff
deleted file mode 100755
index 630754abf..000000000
Binary files a/presentation/lib/font/source-sans-pro/source-sans-pro-regular.woff and /dev/null differ
diff --git a/presentation/lib/font/source-sans-pro/source-sans-pro-semibold.eot b/presentation/lib/font/source-sans-pro/source-sans-pro-semibold.eot
deleted file mode 100755
index 1104e074f..000000000
Binary files a/presentation/lib/font/source-sans-pro/source-sans-pro-semibold.eot and /dev/null differ
diff --git a/presentation/lib/font/source-sans-pro/source-sans-pro-semibold.ttf b/presentation/lib/font/source-sans-pro/source-sans-pro-semibold.ttf
deleted file mode 100755
index 6d0253da9..000000000
Binary files a/presentation/lib/font/source-sans-pro/source-sans-pro-semibold.ttf and /dev/null differ
diff --git a/presentation/lib/font/source-sans-pro/source-sans-pro-semibold.woff b/presentation/lib/font/source-sans-pro/source-sans-pro-semibold.woff
deleted file mode 100755
index 8888cf8d4..000000000
Binary files a/presentation/lib/font/source-sans-pro/source-sans-pro-semibold.woff and /dev/null differ
diff --git a/presentation/lib/font/source-sans-pro/source-sans-pro-semibolditalic.eot b/presentation/lib/font/source-sans-pro/source-sans-pro-semibolditalic.eot
deleted file mode 100755
index cdf733438..000000000
Binary files a/presentation/lib/font/source-sans-pro/source-sans-pro-semibolditalic.eot and /dev/null differ
diff --git a/presentation/lib/font/source-sans-pro/source-sans-pro-semibolditalic.ttf b/presentation/lib/font/source-sans-pro/source-sans-pro-semibolditalic.ttf
deleted file mode 100755
index 56442992a..000000000
Binary files a/presentation/lib/font/source-sans-pro/source-sans-pro-semibolditalic.ttf and /dev/null differ
diff --git a/presentation/lib/font/source-sans-pro/source-sans-pro-semibolditalic.woff b/presentation/lib/font/source-sans-pro/source-sans-pro-semibolditalic.woff
deleted file mode 100755
index 7c2d3c74f..000000000
Binary files a/presentation/lib/font/source-sans-pro/source-sans-pro-semibolditalic.woff and /dev/null differ
diff --git a/presentation/lib/font/source-sans-pro/source-sans-pro.css b/presentation/lib/font/source-sans-pro/source-sans-pro.css
deleted file mode 100644
index 0707a4f86..000000000
--- a/presentation/lib/font/source-sans-pro/source-sans-pro.css
+++ /dev/null
@@ -1,39 +0,0 @@
-@font-face {
- font-family: 'Source Sans Pro';
- src: url('source-sans-pro-regular.eot');
- src: url('source-sans-pro-regular.eot?#iefix') format('embedded-opentype'),
- url('source-sans-pro-regular.woff') format('woff'),
- url('source-sans-pro-regular.ttf') format('truetype');
- font-weight: normal;
- font-style: normal;
-}
-
-@font-face {
- font-family: 'Source Sans Pro';
- src: url('source-sans-pro-italic.eot');
- src: url('source-sans-pro-italic.eot?#iefix') format('embedded-opentype'),
- url('source-sans-pro-italic.woff') format('woff'),
- url('source-sans-pro-italic.ttf') format('truetype');
- font-weight: normal;
- font-style: italic;
-}
-
-@font-face {
- font-family: 'Source Sans Pro';
- src: url('source-sans-pro-semibold.eot');
- src: url('source-sans-pro-semibold.eot?#iefix') format('embedded-opentype'),
- url('source-sans-pro-semibold.woff') format('woff'),
- url('source-sans-pro-semibold.ttf') format('truetype');
- font-weight: 600;
- font-style: normal;
-}
-
-@font-face {
- font-family: 'Source Sans Pro';
- src: url('source-sans-pro-semibolditalic.eot');
- src: url('source-sans-pro-semibolditalic.eot?#iefix') format('embedded-opentype'),
- url('source-sans-pro-semibolditalic.woff') format('woff'),
- url('source-sans-pro-semibolditalic.ttf') format('truetype');
- font-weight: 600;
- font-style: italic;
-}
\ No newline at end of file
diff --git a/presentation/lib/js/classList.js b/presentation/lib/js/classList.js
deleted file mode 100644
index 44f2b4cec..000000000
--- a/presentation/lib/js/classList.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/*! @source http://purl.eligrey.com/github/classList.js/blob/master/classList.js*/
-if(typeof document!=="undefined"&&!("classList" in document.createElement("a"))){(function(j){var a="classList",f="prototype",m=(j.HTMLElement||j.Element)[f],b=Object,k=String[f].trim||function(){return this.replace(/^\s+|\s+$/g,"")},c=Array[f].indexOf||function(q){var p=0,o=this.length;for(;p
- Copyright Tero Piirainen (tipiirai)
- License MIT / http://bit.ly/mit-license
- Version 0.96
-
- http://headjs.com
-*/(function(a){function z(){d||(d=!0,s(e,function(a){p(a)}))}function y(c,d){var e=a.createElement("script");e.type="text/"+(c.type||"javascript"),e.src=c.src||c,e.async=!1,e.onreadystatechange=e.onload=function(){var a=e.readyState;!d.done&&(!a||/loaded|complete/.test(a))&&(d.done=!0,d())},(a.body||b).appendChild(e)}function x(a,b){if(a.state==o)return b&&b();if(a.state==n)return k.ready(a.name,b);if(a.state==m)return a.onpreload.push(function(){x(a,b)});a.state=n,y(a.url,function(){a.state=o,b&&b(),s(g[a.name],function(a){p(a)}),u()&&d&&s(g.ALL,function(a){p(a)})})}function w(a,b){a.state===undefined&&(a.state=m,a.onpreload=[],y({src:a.url,type:"cache"},function(){v(a)}))}function v(a){a.state=l,s(a.onpreload,function(a){a.call()})}function u(a){a=a||h;var b;for(var c in a){if(a.hasOwnProperty(c)&&a[c].state!=o)return!1;b=!0}return b}function t(a){return Object.prototype.toString.call(a)=="[object Function]"}function s(a,b){if(!!a){typeof a=="object"&&(a=[].slice.call(a));for(var c=0;c/g,">");
- }
-
- // re-highlight when focus is lost (for edited code)
- element.addEventListener( 'focusout', function( event ) {
- hljs.highlightBlock( event.currentTarget );
- }, false );
- }
- }
-})();
-// END CUSTOM REVEAL.JS INTEGRATION
-
-// highlight.js v8.2 with support for all available languages
-
-var hljs=new function(){function j(v){return v.replace(/&/gm,"&").replace(//gm,">")}function t(v){return v.nodeName.toLowerCase()}function h(w,x){var v=w&&w.exec(x);return v&&v.index==0}function r(w){var v=(w.className+" "+(w.parentNode?w.parentNode.className:"")).split(/\s+/);v=v.map(function(x){return x.replace(/^lang(uage)?-/,"")});return v.filter(function(x){return i(x)||/no(-?)highlight/.test(x)})[0]}function o(x,y){var v={};for(var w in x){v[w]=x[w]}if(y){for(var w in y){v[w]=y[w]}}return v}function u(x){var v=[];(function w(y,z){for(var A=y.firstChild;A;A=A.nextSibling){if(A.nodeType==3){z+=A.nodeValue.length}else{if(A.nodeType==1){v.push({event:"start",offset:z,node:A});z=w(A,z);if(!t(A).match(/br|hr|img|input/)){v.push({event:"stop",offset:z,node:A})}}}}return z})(x,0);return v}function q(w,y,C){var x=0;var F="";var z=[];function B(){if(!w.length||!y.length){return w.length?w:y}if(w[0].offset!=y[0].offset){return(w[0].offset"}function E(G){F+=""+t(G)+">"}function v(G){(G.event=="start"?A:E)(G.node)}while(w.length||y.length){var D=B();F+=j(C.substr(x,D[0].offset-x));x=D[0].offset;if(D==w){z.reverse().forEach(E);do{v(D.splice(0,1)[0]);D=B()}while(D==w&&D.length&&D[0].offset==x);z.reverse().forEach(A)}else{if(D[0].event=="start"){z.push(D[0].node)}else{z.pop()}v(D.splice(0,1)[0])}}return F+j(C.substr(x))}function m(y){function v(z){return(z&&z.source)||z}function w(A,z){return RegExp(v(A),"m"+(y.cI?"i":"")+(z?"g":""))}function x(D,C){if(D.compiled){return}D.compiled=true;D.k=D.k||D.bK;if(D.k){var z={};var E=function(G,F){if(y.cI){F=F.toLowerCase()}F.split(" ").forEach(function(H){var I=H.split("|");z[I[0]]=[G,I[1]?Number(I[1]):1]})};if(typeof D.k=="string"){E("keyword",D.k)}else{Object.keys(D.k).forEach(function(F){E(F,D.k[F])})}D.k=z}D.lR=w(D.l||/\b[A-Za-z0-9_]+\b/,true);if(C){if(D.bK){D.b="\\b("+D.bK.split(" ").join("|")+")\\b"}if(!D.b){D.b=/\B|\b/}D.bR=w(D.b);if(!D.e&&!D.eW){D.e=/\B|\b/}if(D.e){D.eR=w(D.e)}D.tE=v(D.e)||"";if(D.eW&&C.tE){D.tE+=(D.e?"|":"")+C.tE}}if(D.i){D.iR=w(D.i)}if(D.r===undefined){D.r=1}if(!D.c){D.c=[]}var B=[];D.c.forEach(function(F){if(F.v){F.v.forEach(function(G){B.push(o(F,G))})}else{B.push(F=="self"?D:F)}});D.c=B;D.c.forEach(function(F){x(F,D)});if(D.starts){x(D.starts,C)}var A=D.c.map(function(F){return F.bK?"\\.?("+F.b+")\\.?":F.b}).concat([D.tE,D.i]).map(v).filter(Boolean);D.t=A.length?w(A.join("|"),true):{exec:function(F){return null}}}x(y)}function c(T,L,J,R){function v(V,W){for(var U=0;U";V+=aa+'">';return V+Y+Z}function N(){if(!I.k){return j(C)}var U="";var X=0;I.lR.lastIndex=0;var V=I.lR.exec(C);while(V){U+=j(C.substr(X,V.index-X));var W=E(I,V);if(W){H+=W[1];U+=w(W[0],j(V[0]))}else{U+=j(V[0])}X=I.lR.lastIndex;V=I.lR.exec(C)}return U+j(C.substr(X))}function F(){if(I.sL&&!f[I.sL]){return j(C)}var U=I.sL?c(I.sL,C,true,S):e(C);if(I.r>0){H+=U.r}if(I.subLanguageMode=="continuous"){S=U.top}return w(U.language,U.value,false,true)}function Q(){return I.sL!==undefined?F():N()}function P(W,V){var U=W.cN?w(W.cN,"",true):"";if(W.rB){D+=U;C=""}else{if(W.eB){D+=j(V)+U;C=""}else{D+=U;C=V}}I=Object.create(W,{parent:{value:I}})}function G(U,Y){C+=U;if(Y===undefined){D+=Q();return 0}var W=v(Y,I);if(W){D+=Q();P(W,Y);return W.rB?0:Y.length}var X=z(I,Y);if(X){var V=I;if(!(V.rE||V.eE)){C+=Y}D+=Q();do{if(I.cN){D+=""}H+=I.r;I=I.parent}while(I!=X.parent);if(V.eE){D+=j(Y)}C="";if(X.starts){P(X.starts,"")}return V.rE?0:Y.length}if(A(Y,I)){throw new Error('Illegal lexeme "'+Y+'" for mode "'+(I.cN||"")+'"')}C+=Y;return Y.length||1}var M=i(T);if(!M){throw new Error('Unknown language: "'+T+'"')}m(M);var I=R||M;var S;var D="";for(var K=I;K!=M;K=K.parent){if(K.cN){D=w(K.cN,"",true)+D}}var C="";var H=0;try{var B,y,x=0;while(true){I.t.lastIndex=x;B=I.t.exec(L);if(!B){break}y=G(L.substr(x,B.index-x),B[0]);x=B.index+y}G(L.substr(x));for(var K=I;K.parent;K=K.parent){if(K.cN){D+=""}}return{r:H,value:D,language:T,top:I}}catch(O){if(O.message.indexOf("Illegal")!=-1){return{r:0,value:j(L)}}else{throw O}}}function e(y,x){x=x||b.languages||Object.keys(f);var v={r:0,value:j(y)};var w=v;x.forEach(function(z){if(!i(z)){return}var A=c(z,y,false);A.language=z;if(A.r>w.r){w=A}if(A.r>v.r){w=v;v=A}});if(w.language){v.second_best=w}return v}function g(v){if(b.tabReplace){v=v.replace(/^((<[^>]+>|\t)+)/gm,function(w,z,y,x){return z.replace(/\t/g,b.tabReplace)})}if(b.useBR){v=v.replace(/\n/g," ")}return v}function p(A){var B=r(A);if(/no(-?)highlight/.test(B)){return}var y;if(b.useBR){y=document.createElementNS("http://www.w3.org/1999/xhtml","div");y.innerHTML=A.innerHTML.replace(/\n/g,"").replace(/ /g,"\n")}else{y=A}var z=y.textContent;var v=B?c(B,z,true):e(z);var x=u(y);if(x.length){var w=document.createElementNS("http://www.w3.org/1999/xhtml","div");w.innerHTML=v.value;v.value=q(x,u(w),z)}v.value=g(v.value);A.innerHTML=v.value;A.className+=" hljs "+(!B&&v.language||"");A.result={language:v.language,re:v.r};if(v.second_best){A.second_best={language:v.second_best.language,re:v.second_best.r}}}var b={classPrefix:"hljs-",tabReplace:null,useBR:false,languages:undefined};function s(v){b=o(b,v)}function l(){if(l.called){return}l.called=true;var v=document.querySelectorAll("pre code");Array.prototype.forEach.call(v,p)}function a(){addEventListener("DOMContentLoaded",l,false);addEventListener("load",l,false)}var f={};var n={};function d(v,x){var w=f[v]=x(this);if(w.aliases){w.aliases.forEach(function(y){n[y]=v})}}function k(){return Object.keys(f)}function i(v){return f[v]||f[n[v]]}this.highlight=c;this.highlightAuto=e;this.fixMarkup=g;this.highlightBlock=p;this.configure=s;this.initHighlighting=l;this.initHighlightingOnLoad=a;this.registerLanguage=d;this.listLanguages=k;this.getLanguage=i;this.inherit=o;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE]};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE]};this.PWM={b:/\b(a|an|the|are|I|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such)\b/};this.CLCM={cN:"comment",b:"//",e:"$",c:[this.PWM]};this.CBCM={cN:"comment",b:"/\\*",e:"\\*/",c:[this.PWM]};this.HCM={cN:"comment",b:"#",e:"$",c:[this.PWM]};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.CSSNM={cN:"number",b:this.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0};this.RM={cN:"regexp",b:/\//,e:/\/[gim]*/,i:/\n/,c:[this.BE,{b:/\[/,e:/\]/,r:0,c:[this.BE]}]};this.TM={cN:"title",b:this.IR,r:0};this.UTM={cN:"title",b:this.UIR,r:0}}();hljs.registerLanguage("bash",function(b){var a={cN:"variable",v:[{b:/\$[\w\d#@][\w\d_]*/},{b:/\$\{(.*?)\}/}]};var d={cN:"string",b:/"/,e:/"/,c:[b.BE,a,{cN:"variable",b:/\$\(/,e:/\)/,c:[b.BE]}]};var c={cN:"string",b:/'/,e:/'/};return{aliases:["sh","zsh"],l:/-?[a-z\.]+/,k:{keyword:"if then else elif fi for break continue while in do done exit return set declare case esac export exec",literal:"true false",built_in:"printf echo read cd pwd pushd popd dirs let eval unset typeset readonly getopts source shopt caller type hash bind help sudo",operator:"-ne -eq -lt -gt -f -d -e -s -l -a"},c:[{cN:"shebang",b:/^#![^\n]+sh\s*$/,r:10},{cN:"function",b:/\w[\w\d_]*\s*\(\s*\)\s*\{/,rB:true,c:[b.inherit(b.TM,{b:/\w[\w\d_]*/})],r:0},b.HCM,b.NM,d,c,a]}});hljs.registerLanguage("fix",function(a){return{c:[{b:/[^\u2401\u0001]+/,e:/[\u2401\u0001]/,eE:true,rB:true,rE:false,c:[{b:/([^\u2401\u0001=]+)/,e:/=([^\u2401\u0001=]+)/,rE:true,rB:false,cN:"attribute"},{b:/=/,e:/([\u2401\u0001])/,eE:true,eB:true,cN:"string"}]}],cI:true}});hljs.registerLanguage("nsis",function(a){var c={cN:"symbol",b:"\\$(ADMINTOOLS|APPDATA|CDBURN_AREA|CMDLINE|COMMONFILES32|COMMONFILES64|COMMONFILES|COOKIES|DESKTOP|DOCUMENTS|EXEDIR|EXEFILE|EXEPATH|FAVORITES|FONTS|HISTORY|HWNDPARENT|INSTDIR|INTERNET_CACHE|LANGUAGE|LOCALAPPDATA|MUSIC|NETHOOD|OUTDIR|PICTURES|PLUGINSDIR|PRINTHOOD|PROFILE|PROGRAMFILES32|PROGRAMFILES64|PROGRAMFILES|QUICKLAUNCH|RECENT|RESOURCES_LOCALIZED|RESOURCES|SENDTO|SMPROGRAMS|SMSTARTUP|STARTMENU|SYSDIR|TEMP|TEMPLATES|VIDEOS|WINDIR)"};var b={cN:"constant",b:"\\$+{[a-zA-Z0-9_]+}"};var f={cN:"variable",b:"\\$+[a-zA-Z0-9_]+",i:"\\(\\){}"};var e={cN:"constant",b:"\\$+\\([a-zA-Z0-9_]+\\)"};var g={cN:"params",b:"(ARCHIVE|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_OFFLINE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY|HKCR|HKCU|HKDD|HKEY_CLASSES_ROOT|HKEY_CURRENT_CONFIG|HKEY_CURRENT_USER|HKEY_DYN_DATA|HKEY_LOCAL_MACHINE|HKEY_PERFORMANCE_DATA|HKEY_USERS|HKLM|HKPD|HKU|IDABORT|IDCANCEL|IDIGNORE|IDNO|IDOK|IDRETRY|IDYES|MB_ABORTRETRYIGNORE|MB_DEFBUTTON1|MB_DEFBUTTON2|MB_DEFBUTTON3|MB_DEFBUTTON4|MB_ICONEXCLAMATION|MB_ICONINFORMATION|MB_ICONQUESTION|MB_ICONSTOP|MB_OK|MB_OKCANCEL|MB_RETRYCANCEL|MB_RIGHT|MB_RTLREADING|MB_SETFOREGROUND|MB_TOPMOST|MB_USERICON|MB_YESNO|NORMAL|OFFLINE|READONLY|SHCTX|SHELL_CONTEXT|SYSTEM|TEMPORARY)"};var d={cN:"constant",b:"\\!(addincludedir|addplugindir|appendfile|cd|define|delfile|echo|else|endif|error|execute|finalize|getdllversionsystem|ifdef|ifmacrodef|ifmacrondef|ifndef|if|include|insertmacro|macroend|macro|packhdr|searchparse|searchreplace|tempfile|undef|verbose|warning)"};return{cI:false,k:{keyword:"Abort AddBrandingImage AddSize AllowRootDirInstall AllowSkipFiles AutoCloseWindow BGFont BGGradient BrandingText BringToFront Call CallInstDLL Caption ChangeUI CheckBitmap ClearErrors CompletedText ComponentText CopyFiles CRCCheck CreateDirectory CreateFont CreateShortCut Delete DeleteINISec DeleteINIStr DeleteRegKey DeleteRegValue DetailPrint DetailsButtonText DirText DirVar DirVerify EnableWindow EnumRegKey EnumRegValue Exch Exec ExecShell ExecWait ExpandEnvStrings File FileBufSize FileClose FileErrorText FileOpen FileRead FileReadByte FileReadUTF16LE FileReadWord FileSeek FileWrite FileWriteByte FileWriteUTF16LE FileWriteWord FindClose FindFirst FindNext FindWindow FlushINI FunctionEnd GetCurInstType GetCurrentAddress GetDlgItem GetDLLVersion GetDLLVersionLocal GetErrorLevel GetFileTime GetFileTimeLocal GetFullPathName GetFunctionAddress GetInstDirError GetLabelAddress GetTempFileName Goto HideWindow Icon IfAbort IfErrors IfFileExists IfRebootFlag IfSilent InitPluginsDir InstallButtonText InstallColors InstallDir InstallDirRegKey InstProgressFlags InstType InstTypeGetText InstTypeSetText IntCmp IntCmpU IntFmt IntOp IsWindow LangString LicenseBkColor LicenseData LicenseForceSelection LicenseLangString LicenseText LoadLanguageFile LockWindow LogSet LogText ManifestDPIAware ManifestSupportedOS MessageBox MiscButtonText Name Nop OutFile Page PageCallbacks PageExEnd Pop Push Quit ReadEnvStr ReadINIStr ReadRegDWORD ReadRegStr Reboot RegDLL Rename RequestExecutionLevel ReserveFile Return RMDir SearchPath SectionEnd SectionGetFlags SectionGetInstTypes SectionGetSize SectionGetText SectionGroupEnd SectionIn SectionSetFlags SectionSetInstTypes SectionSetSize SectionSetText SendMessage SetAutoClose SetBrandingImage SetCompress SetCompressor SetCompressorDictSize SetCtlColors SetCurInstType SetDatablockOptimize SetDateSave SetDetailsPrint SetDetailsView SetErrorLevel SetErrors SetFileAttributes SetFont SetOutPath SetOverwrite SetPluginUnload SetRebootFlag SetRegView SetShellVarContext SetSilent ShowInstDetails ShowUninstDetails ShowWindow SilentInstall SilentUnInstall Sleep SpaceTexts StrCmp StrCmpS StrCpy StrLen SubCaption SubSectionEnd Unicode UninstallButtonText UninstallCaption UninstallIcon UninstallSubCaption UninstallText UninstPage UnRegDLL Var VIAddVersionKey VIFileVersion VIProductVersion WindowIcon WriteINIStr WriteRegBin WriteRegDWORD WriteRegExpandStr WriteRegStr WriteUninstaller XPStyle",literal:"admin all auto both colored current false force hide highest lastused leave listonly none normal notset off on open print show silent silentlog smooth textonly true user "},c:[a.HCM,a.CBCM,{cN:"string",b:'"',e:'"',i:"\\n",c:[{cN:"symbol",b:"\\$(\\\\(n|r|t)|\\$)"},c,b,f,e]},{cN:"comment",b:";",e:"$",r:0},{cN:"function",bK:"Function PageEx Section SectionGroup SubSection",e:"$"},d,b,f,e,g,a.NM,{cN:"literal",b:a.IR+"::"+a.IR}]}});hljs.registerLanguage("haxe",function(a){var c="[a-zA-Z_$][a-zA-Z0-9_$]*";var b="([*]|[a-zA-Z_$][a-zA-Z0-9_$]*)";return{aliases:["hx"],k:{keyword:"break callback case cast catch class continue default do dynamic else enum extends extern for function here if implements import in inline interface never new override package private public return static super switch this throw trace try typedef untyped using var while",literal:"true false null"},c:[a.ASM,a.QSM,a.CLCM,a.CBCM,a.CNM,{cN:"class",bK:"class interface",e:"{",eE:true,c:[{bK:"extends implements"},a.TM]},{cN:"preprocessor",b:"#",e:"$",k:"if else elseif end error"},{cN:"function",bK:"function",e:"[{;]",eE:true,i:"\\S",c:[a.TM,{cN:"params",b:"\\(",e:"\\)",c:[a.ASM,a.QSM,a.CLCM,a.CBCM]},{cN:"type",b:":",e:b,r:10}]}]}});hljs.registerLanguage("erlang",function(i){var c="[a-z'][a-zA-Z0-9_']*";var o="("+c+":"+c+"|"+c+")";var f={keyword:"after and andalso|10 band begin bnot bor bsl bzr bxor case catch cond div end fun if let not of orelse|10 query receive rem try when xor",literal:"false true"};var l={cN:"comment",b:"%",e:"$"};var e={cN:"number",b:"\\b(\\d+#[a-fA-F0-9]+|\\d+(\\.\\d+)?([eE][-+]?\\d+)?)",r:0};var g={b:"fun\\s+"+c+"/\\d+"};var n={b:o+"\\(",e:"\\)",rB:true,r:0,c:[{cN:"function_name",b:o,r:0},{b:"\\(",e:"\\)",eW:true,rE:true,r:0}]};var h={cN:"tuple",b:"{",e:"}",r:0};var a={cN:"variable",b:"\\b_([A-Z][A-Za-z0-9_]*)?",r:0};var m={cN:"variable",b:"[A-Z][a-zA-Z0-9_]*",r:0};var b={b:"#"+i.UIR,r:0,rB:true,c:[{cN:"record_name",b:"#"+i.UIR,r:0},{b:"{",e:"}",r:0}]};var k={bK:"fun receive if try case",e:"end",k:f};k.c=[l,g,i.inherit(i.ASM,{cN:""}),k,n,i.QSM,e,h,a,m,b];var j=[l,g,k,n,i.QSM,e,h,a,m,b];n.c[1].c=j;h.c=j;b.c[1].c=j;var d={cN:"params",b:"\\(",e:"\\)",c:j};return{aliases:["erl"],k:f,i:"(|\\*=|\\+=|-=|/\\*|\\*/|\\(\\*|\\*\\))",c:[{cN:"function",b:"^"+c+"\\s*\\(",e:"->",rB:true,i:"\\(|#|//|/\\*|\\\\|:|;",c:[d,i.inherit(i.TM,{b:c})],starts:{e:";|\\.",k:f,c:j}},l,{cN:"pp",b:"^-",e:"\\.",r:0,eE:true,rB:true,l:"-"+i.IR,k:"-module -record -undef -export -ifdef -ifndef -author -copyright -doc -vsn -import -include -include_lib -compile -define -else -endif -file -behaviour -behavior -spec",c:[d]},e,i.QSM,b,a,m,h,{b:/\.$/}]}});hljs.registerLanguage("cs",function(c){var b="abstract as base bool break byte case catch char checked const continue decimal default delegate do double else enum event explicit extern false finally fixed float for foreach goto if implicit in int interface internal is lock long new null object operator out override params private protected public readonly ref return sbyte sealed short sizeof stackalloc static string struct switch this throw true try typeof uint ulong unchecked unsafe ushort using virtual volatile void while async await protected public private internal ascending descending from get group into join let orderby partial select set value var where yield";var a=c.IR+"(<"+c.IR+">)?";return{aliases:["csharp"],k:b,i:/::/,c:[{cN:"comment",b:"///",e:"$",rB:true,c:[{cN:"xmlDocTag",v:[{b:"///",r:0},{b:""},{b:"?",e:">"}]}]},c.CLCM,c.CBCM,{cN:"preprocessor",b:"#",e:"$",k:"if else elif endif define undef warning error line region endregion pragma checksum"},{cN:"string",b:'@"',e:'"',c:[{b:'""'}]},c.ASM,c.QSM,c.CNM,{bK:"class namespace interface",e:/[{;=]/,i:/[^\s:]/,c:[c.TM,c.CLCM,c.CBCM]},{bK:"new",e:/\s/,r:0},{cN:"function",b:"("+a+"\\s+)+"+c.IR+"\\s*\\(",rB:true,e:/[{;=]/,eE:true,k:b,c:[{b:c.IR+"\\s*\\(",rB:true,c:[c.TM]},{cN:"params",b:/\(/,e:/\)/,k:b,c:[c.ASM,c.QSM,c.CNM,c.CBCM]},c.CLCM,c.CBCM]}]}});hljs.registerLanguage("protobuf",function(a){return{k:{keyword:"package import option optional required repeated group",built_in:"double float int32 int64 uint32 uint64 sint32 sint64 fixed32 fixed64 sfixed32 sfixed64 bool string bytes",literal:"true false"},c:[a.QSM,a.NM,a.CLCM,{cN:"class",bK:"message enum service",e:/\{/,i:/\n/,c:[a.inherit(a.TM,{starts:{eW:true,eE:true}})]},{cN:"function",bK:"rpc",e:/;/,eE:true,k:"rpc returns"},{cN:"constant",b:/^\s*[A-Z_]+/,e:/\s*=/,eE:true}]}});hljs.registerLanguage("vim",function(a){return{l:/[!#@\w]+/,k:{keyword:"N|0 P|0 X|0 a|0 ab abc abo al am an|0 ar arga argd arge argdo argg argl argu as au aug aun b|0 bN ba bad bd be bel bf bl bm bn bo bp br brea breaka breakd breakl bro bufdo buffers bun bw c|0 cN cNf ca cabc caddb cad caddf cal cat cb cc ccl cd ce cex cf cfir cgetb cgete cg changes chd che checkt cl cla clo cm cmapc cme cn cnew cnf cno cnorea cnoreme co col colo com comc comp con conf cope cp cpf cq cr cs cst cu cuna cunme cw d|0 delm deb debugg delc delf dif diffg diffo diffp diffpu diffs diffthis dig di dl dell dj dli do doautoa dp dr ds dsp e|0 ea ec echoe echoh echom echon el elsei em en endfo endf endt endw ene ex exe exi exu f|0 files filet fin fina fini fir fix fo foldc foldd folddoc foldo for fu g|0 go gr grepa gu gv ha h|0 helpf helpg helpt hi hid his i|0 ia iabc if ij il im imapc ime ino inorea inoreme int is isp iu iuna iunme j|0 ju k|0 keepa kee keepj lN lNf l|0 lad laddb laddf la lan lat lb lc lch lcl lcs le lefta let lex lf lfir lgetb lgete lg lgr lgrepa lh ll lla lli lmak lm lmapc lne lnew lnf ln loadk lo loc lockv lol lope lp lpf lr ls lt lu lua luad luaf lv lvimgrepa lw m|0 ma mak map mapc marks mat me menut mes mk mks mksp mkv mkvie mod mz mzf nbc nb nbs n|0 new nm nmapc nme nn nnoreme noa no noh norea noreme norm nu nun nunme ol o|0 om omapc ome on ono onoreme opt ou ounme ow p|0 profd prof pro promptr pc ped pe perld po popu pp pre prev ps pt ptN ptf ptj ptl ptn ptp ptr pts pu pw py3 python3 py3d py3f py pyd pyf q|0 quita qa r|0 rec red redi redr redraws reg res ret retu rew ri rightb rub rubyd rubyf rund ru rv s|0 sN san sa sal sav sb sbN sba sbf sbl sbm sbn sbp sbr scrip scripte scs se setf setg setl sf sfir sh sim sig sil sl sla sm smap smapc sme sn sni sno snor snoreme sor so spelld spe spelli spellr spellu spellw sp spr sre st sta startg startr star stopi stj sts sun sunm sunme sus sv sw sy synti sync t|0 tN tabN tabc tabdo tabe tabf tabfir tabl tabm tabnew tabn tabo tabp tabr tabs tab ta tags tc tcld tclf te tf th tj tl tm tn to tp tr try ts tu u|0 undoj undol una unh unl unlo unm unme uns up v|0 ve verb vert vim vimgrepa vi viu vie vm vmapc vme vne vn vnoreme vs vu vunme windo w|0 wN wa wh wi winc winp wn wp wq wqa ws wu wv x|0 xa xmapc xm xme xn xnoreme xu xunme y|0 z|0 ~ Next Print append abbreviate abclear aboveleft all amenu anoremenu args argadd argdelete argedit argglobal arglocal argument ascii autocmd augroup aunmenu buffer bNext ball badd bdelete behave belowright bfirst blast bmodified bnext botright bprevious brewind break breakadd breakdel breaklist browse bunload bwipeout change cNext cNfile cabbrev cabclear caddbuffer caddexpr caddfile call catch cbuffer cclose center cexpr cfile cfirst cgetbuffer cgetexpr cgetfile chdir checkpath checktime clist clast close cmap cmapclear cmenu cnext cnewer cnfile cnoremap cnoreabbrev cnoremenu copy colder colorscheme command comclear compiler continue confirm copen cprevious cpfile cquit crewind cscope cstag cunmap cunabbrev cunmenu cwindow delete delmarks debug debuggreedy delcommand delfunction diffupdate diffget diffoff diffpatch diffput diffsplit digraphs display deletel djump dlist doautocmd doautoall deletep drop dsearch dsplit edit earlier echo echoerr echohl echomsg else elseif emenu endif endfor endfunction endtry endwhile enew execute exit exusage file filetype find finally finish first fixdel fold foldclose folddoopen folddoclosed foldopen function global goto grep grepadd gui gvim hardcopy help helpfind helpgrep helptags highlight hide history insert iabbrev iabclear ijump ilist imap imapclear imenu inoremap inoreabbrev inoremenu intro isearch isplit iunmap iunabbrev iunmenu join jumps keepalt keepmarks keepjumps lNext lNfile list laddexpr laddbuffer laddfile last language later lbuffer lcd lchdir lclose lcscope left leftabove lexpr lfile lfirst lgetbuffer lgetexpr lgetfile lgrep lgrepadd lhelpgrep llast llist lmake lmap lmapclear lnext lnewer lnfile lnoremap loadkeymap loadview lockmarks lockvar lolder lopen lprevious lpfile lrewind ltag lunmap luado luafile lvimgrep lvimgrepadd lwindow move mark make mapclear match menu menutranslate messages mkexrc mksession mkspell mkvimrc mkview mode mzscheme mzfile nbclose nbkey nbsart next nmap nmapclear nmenu nnoremap nnoremenu noautocmd noremap nohlsearch noreabbrev noremenu normal number nunmap nunmenu oldfiles open omap omapclear omenu only onoremap onoremenu options ounmap ounmenu ownsyntax print profdel profile promptfind promptrepl pclose pedit perl perldo pop popup ppop preserve previous psearch ptag ptNext ptfirst ptjump ptlast ptnext ptprevious ptrewind ptselect put pwd py3do py3file python pydo pyfile quit quitall qall read recover redo redir redraw redrawstatus registers resize retab return rewind right rightbelow ruby rubydo rubyfile rundo runtime rviminfo substitute sNext sandbox sargument sall saveas sbuffer sbNext sball sbfirst sblast sbmodified sbnext sbprevious sbrewind scriptnames scriptencoding scscope set setfiletype setglobal setlocal sfind sfirst shell simalt sign silent sleep slast smagic smapclear smenu snext sniff snomagic snoremap snoremenu sort source spelldump spellgood spellinfo spellrepall spellundo spellwrong split sprevious srewind stop stag startgreplace startreplace startinsert stopinsert stjump stselect sunhide sunmap sunmenu suspend sview swapname syntax syntime syncbind tNext tabNext tabclose tabedit tabfind tabfirst tablast tabmove tabnext tabonly tabprevious tabrewind tag tcl tcldo tclfile tearoff tfirst throw tjump tlast tmenu tnext topleft tprevious trewind tselect tunmenu undo undojoin undolist unabbreviate unhide unlet unlockvar unmap unmenu unsilent update vglobal version verbose vertical vimgrep vimgrepadd visual viusage view vmap vmapclear vmenu vnew vnoremap vnoremenu vsplit vunmap vunmenu write wNext wall while winsize wincmd winpos wnext wprevious wqall wsverb wundo wviminfo xit xall xmapclear xmap xmenu xnoremap xnoremenu xunmap xunmenu yank",built_in:"abs acos add and append argc argidx argv asin atan atan2 browse browsedir bufexists buflisted bufloaded bufname bufnr bufwinnr byte2line byteidx call ceil changenr char2nr cindent clearmatches col complete complete_add complete_check confirm copy cos cosh count cscope_connection cursor deepcopy delete did_filetype diff_filler diff_hlID empty escape eval eventhandler executable exists exp expand extend feedkeys filereadable filewritable filter finddir findfile float2nr floor fmod fnameescape fnamemodify foldclosed foldclosedend foldlevel foldtext foldtextresult foreground function garbagecollect get getbufline getbufvar getchar getcharmod getcmdline getcmdpos getcmdtype getcwd getfontname getfperm getfsize getftime getftype getline getloclist getmatches getpid getpos getqflist getreg getregtype gettabvar gettabwinvar getwinposx getwinposy getwinvar glob globpath has has_key haslocaldir hasmapto histadd histdel histget histnr hlexists hlID hostname iconv indent index input inputdialog inputlist inputrestore inputsave inputsecret insert invert isdirectory islocked items join keys len libcall libcallnr line line2byte lispindent localtime log log10 luaeval map maparg mapcheck match matchadd matcharg matchdelete matchend matchlist matchstr max min mkdir mode mzeval nextnonblank nr2char or pathshorten pow prevnonblank printf pumvisible py3eval pyeval range readfile reltime reltimestr remote_expr remote_foreground remote_peek remote_read remote_send remove rename repeat resolve reverse round screenattr screenchar screencol screenrow search searchdecl searchpair searchpairpos searchpos server2client serverlist setbufvar setcmdpos setline setloclist setmatches setpos setqflist setreg settabvar settabwinvar setwinvar sha256 shellescape shiftwidth simplify sin sinh sort soundfold spellbadword spellsuggest split sqrt str2float str2nr strchars strdisplaywidth strftime stridx string strlen strpart strridx strtrans strwidth submatch substitute synconcealed synID synIDattr synIDtrans synstack system tabpagebuflist tabpagenr tabpagewinnr tagfiles taglist tan tanh tempname tolower toupper tr trunc type undofile undotree values virtcol visualmode wildmenumode winbufnr wincol winheight winline winnr winrestcmd winrestview winsaveview winwidth writefile xor"},i:/[{:]/,c:[a.NM,a.ASM,{cN:"string",b:/"((\\")|[^"\n])*("|\n)/},{cN:"variable",b:/[bwtglsav]:[\w\d_]*/},{cN:"function",bK:"function function!",e:"$",r:0,c:[a.TM,{cN:"params",b:"\\(",e:"\\)"}]}]}});hljs.registerLanguage("brainfuck",function(b){var a={cN:"literal",b:"[\\+\\-]",r:0};return{aliases:["bf"],c:[{cN:"comment",b:"[^\\[\\]\\.,\\+\\-<> \r\n]",rE:true,e:"[\\[\\]\\.,\\+\\-<> \r\n]",r:0},{cN:"title",b:"[\\[\\]]",r:0},{cN:"string",b:"[\\.,]",r:0},{b:/\+\+|\-\-/,rB:true,c:[a]},a]}});hljs.registerLanguage("ruby",function(f){var j="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?";var i="and false then defined module in return redo if BEGIN retry end for true self when next until do begin unless END rescue nil else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor";var b={cN:"yardoctag",b:"@[A-Za-z]+"};var c={cN:"value",b:"#<",e:">"};var k={cN:"comment",v:[{b:"#",e:"$",c:[b]},{b:"^\\=begin",e:"^\\=end",c:[b],r:10},{b:"^__END__",e:"\\n$"}]};var d={cN:"subst",b:"#\\{",e:"}",k:i};var e={cN:"string",c:[f.BE,d],v:[{b:/'/,e:/'/},{b:/"/,e:/"/},{b:"%[qw]?\\(",e:"\\)"},{b:"%[qw]?\\[",e:"\\]"},{b:"%[qw]?{",e:"}"},{b:"%[qw]?<",e:">"},{b:"%[qw]?/",e:"/"},{b:"%[qw]?%",e:"%"},{b:"%[qw]?-",e:"-"},{b:"%[qw]?\\|",e:"\\|"},{b:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/}]};var a={cN:"params",b:"\\(",e:"\\)",k:i};var h=[e,c,k,{cN:"class",bK:"class module",e:"$|;",i:/=/,c:[f.inherit(f.TM,{b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{cN:"inheritance",b:"<\\s*",c:[{cN:"parent",b:"("+f.IR+"::)?"+f.IR}]},k]},{cN:"function",bK:"def",e:" |$|;",r:0,c:[f.inherit(f.TM,{b:j}),a,k]},{cN:"constant",b:"(::)?(\\b[A-Z]\\w*(::)?)+",r:0},{cN:"symbol",b:f.UIR+"(\\!|\\?)?:",r:0},{cN:"symbol",b:":",c:[e,{b:j}],r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{cN:"variable",b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{b:"("+f.RSR+")\\s*",c:[c,k,{cN:"regexp",c:[f.BE,d],i:/\n/,v:[{b:"/",e:"/[a-z]*"},{b:"%r{",e:"}[a-z]*"},{b:"%r\\(",e:"\\)[a-z]*"},{b:"%r!",e:"![a-z]*"},{b:"%r\\[",e:"\\][a-z]*"}]}],r:0}];d.c=h;a.c=h;var g=[{b:/^\s*=>/,cN:"status",starts:{e:"$",c:h}},{cN:"prompt",b:/^\S[^=>\n]*>+/,starts:{e:"$",c:h}}];return{aliases:["rb","gemspec","podspec","thor","irb"],k:i,c:[k].concat(g).concat(h)}});hljs.registerLanguage("nimrod",function(a){return{k:{keyword:"addr and as asm bind block break|0 case|0 cast const|0 continue|0 converter discard distinct|10 div do elif else|0 end|0 enum|0 except export finally for from generic if|0 import|0 in include|0 interface is isnot|10 iterator|10 let|0 macro method|10 mixin mod nil not notin|10 object|0 of or out proc|10 ptr raise ref|10 return shl shr static template|10 try|0 tuple type|0 using|0 var|0 when while|0 with without xor yield",literal:"shared guarded stdin stdout stderr result|10 true false"},c:[{cN:"decorator",b:/{\./,e:/\.}/,r:10},{cN:"string",b:/[a-zA-Z]\w*"/,e:/"/,c:[{b:/""/}]},{cN:"string",b:/([a-zA-Z]\w*)?"""/,e:/"""/},{cN:"string",b:/"/,e:/"/,i:/\n/,c:[{b:/\\./}]},{cN:"type",b:/\b[A-Z]\w+\b/,r:0},{cN:"type",b:/\b(int|int8|int16|int32|int64|uint|uint8|uint16|uint32|uint64|float|float32|float64|bool|char|string|cstring|pointer|expr|stmt|void|auto|any|range|array|openarray|varargs|seq|set|clong|culong|cchar|cschar|cshort|cint|csize|clonglong|cfloat|cdouble|clongdouble|cuchar|cushort|cuint|culonglong|cstringarray|semistatic)\b/},{cN:"number",b:/\b(0[xX][0-9a-fA-F][_0-9a-fA-F]*)('?[iIuU](8|16|32|64))?/,r:0},{cN:"number",b:/\b(0o[0-7][_0-7]*)('?[iIuUfF](8|16|32|64))?/,r:0},{cN:"number",b:/\b(0(b|B)[01][_01]*)('?[iIuUfF](8|16|32|64))?/,r:0},{cN:"number",b:/\b(\d[_\d]*)('?[iIuUfF](8|16|32|64))?/,r:0},a.HCM]}});hljs.registerLanguage("rust",function(a){return{aliases:["rs"],k:{keyword:"alignof as be box break const continue crate do else enum extern false fn for if impl in let loop match mod mut offsetof once priv proc pub pure ref return self sizeof static struct super trait true type typeof unsafe unsized use virtual while yield int i8 i16 i32 i64 uint u8 u32 u64 float f32 f64 str char bool",built_in:"assert! assert_eq! bitflags! bytes! cfg! col! concat! concat_idents! debug_assert! debug_assert_eq! env! fail! file! format! format_args! include_bin! include_str! line! local_data_key! module_path! option_env! print! println! select! stringify! try! unimplemented! unreachable! vec! write! writeln!"},l:a.IR+"!?",i:"",c:[a.CLCM,a.CBCM,a.inherit(a.QSM,{i:null}),{cN:"string",b:/r(#*)".*?"\1(?!#)/},{cN:"string",b:/'\\?(x\w{2}|u\w{4}|U\w{8}|.)'/},{b:/'[a-zA-Z_][a-zA-Z0-9_]*/},{cN:"number",b:"\\b(0[xb][A-Za-z0-9_]+|[0-9_]+(\\.[0-9_]+)?([uif](8|16|32|64)?)?)",r:0},{cN:"function",bK:"fn",e:"(\\(|<)",eE:true,c:[a.UTM]},{cN:"preprocessor",b:"#\\[",e:"\\]"},{bK:"type",e:"(=|<)",c:[a.UTM],i:"\\S"},{bK:"trait enum",e:"({|<)",c:[a.UTM],i:"\\S"},{b:a.IR+"::"},{b:"->"}]}});hljs.registerLanguage("ruleslanguage",function(a){return{k:{keyword:"BILL_PERIOD BILL_START BILL_STOP RS_EFFECTIVE_START RS_EFFECTIVE_STOP RS_JURIS_CODE RS_OPCO_CODE INTDADDATTRIBUTE|5 INTDADDVMSG|5 INTDBLOCKOP|5 INTDBLOCKOPNA|5 INTDCLOSE|5 INTDCOUNT|5 INTDCOUNTSTATUSCODE|5 INTDCREATEMASK|5 INTDCREATEDAYMASK|5 INTDCREATEFACTORMASK|5 INTDCREATEHANDLE|5 INTDCREATEOVERRIDEDAYMASK|5 INTDCREATEOVERRIDEMASK|5 INTDCREATESTATUSCODEMASK|5 INTDCREATETOUPERIOD|5 INTDDELETE|5 INTDDIPTEST|5 INTDEXPORT|5 INTDGETERRORCODE|5 INTDGETERRORMESSAGE|5 INTDISEQUAL|5 INTDJOIN|5 INTDLOAD|5 INTDLOADACTUALCUT|5 INTDLOADDATES|5 INTDLOADHIST|5 INTDLOADLIST|5 INTDLOADLISTDATES|5 INTDLOADLISTENERGY|5 INTDLOADLISTHIST|5 INTDLOADRELATEDCHANNEL|5 INTDLOADSP|5 INTDLOADSTAGING|5 INTDLOADUOM|5 INTDLOADUOMDATES|5 INTDLOADUOMHIST|5 INTDLOADVERSION|5 INTDOPEN|5 INTDREADFIRST|5 INTDREADNEXT|5 INTDRECCOUNT|5 INTDRELEASE|5 INTDREPLACE|5 INTDROLLAVG|5 INTDROLLPEAK|5 INTDSCALAROP|5 INTDSCALE|5 INTDSETATTRIBUTE|5 INTDSETDSTPARTICIPANT|5 INTDSETSTRING|5 INTDSETVALUE|5 INTDSETVALUESTATUS|5 INTDSHIFTSTARTTIME|5 INTDSMOOTH|5 INTDSORT|5 INTDSPIKETEST|5 INTDSUBSET|5 INTDTOU|5 INTDTOURELEASE|5 INTDTOUVALUE|5 INTDUPDATESTATS|5 INTDVALUE|5 STDEV INTDDELETEEX|5 INTDLOADEXACTUAL|5 INTDLOADEXCUT|5 INTDLOADEXDATES|5 INTDLOADEX|5 INTDLOADEXRELATEDCHANNEL|5 INTDSAVEEX|5 MVLOAD|5 MVLOADACCT|5 MVLOADACCTDATES|5 MVLOADACCTHIST|5 MVLOADDATES|5 MVLOADHIST|5 MVLOADLIST|5 MVLOADLISTDATES|5 MVLOADLISTHIST|5 IF FOR NEXT DONE SELECT END CALL ABORT CLEAR CHANNEL FACTOR LIST NUMBER OVERRIDE SET WEEK DISTRIBUTIONNODE ELSE WHEN THEN OTHERWISE IENUM CSV INCLUDE LEAVE RIDER SAVE DELETE NOVALUE SECTION WARN SAVE_UPDATE DETERMINANT LABEL REPORT REVENUE EACH IN FROM TOTAL CHARGE BLOCK AND OR CSV_FILE RATE_CODE AUXILIARY_DEMAND UIDACCOUNT RS BILL_PERIOD_SELECT HOURS_PER_MONTH INTD_ERROR_STOP SEASON_SCHEDULE_NAME ACCOUNTFACTOR ARRAYUPPERBOUND CALLSTOREDPROC GETADOCONNECTION GETCONNECT GETDATASOURCE GETQUALIFIER GETUSERID HASVALUE LISTCOUNT LISTOP LISTUPDATE LISTVALUE PRORATEFACTOR RSPRORATE SETBINPATH SETDBMONITOR WQ_OPEN BILLINGHOURS DATE DATEFROMFLOAT DATETIMEFROMSTRING DATETIMETOSTRING DATETOFLOAT DAY DAYDIFF DAYNAME DBDATETIME HOUR MINUTE MONTH MONTHDIFF MONTHHOURS MONTHNAME ROUNDDATE SAMEWEEKDAYLASTYEAR SECOND WEEKDAY WEEKDIFF YEAR YEARDAY YEARSTR COMPSUM HISTCOUNT HISTMAX HISTMIN HISTMINNZ HISTVALUE MAXNRANGE MAXRANGE MINRANGE COMPIKVA COMPKVA COMPKVARFROMKQKW COMPLF IDATTR FLAG LF2KW LF2KWH MAXKW POWERFACTOR READING2USAGE AVGSEASON MAXSEASON MONTHLYMERGE SEASONVALUE SUMSEASON ACCTREADDATES ACCTTABLELOAD CONFIGADD CONFIGGET CREATEOBJECT CREATEREPORT EMAILCLIENT EXPBLKMDMUSAGE EXPMDMUSAGE EXPORT_USAGE FACTORINEFFECT GETUSERSPECIFIEDSTOP INEFFECT ISHOLIDAY RUNRATE SAVE_PROFILE SETREPORTTITLE USEREXIT WATFORRUNRATE TO TABLE ACOS ASIN ATAN ATAN2 BITAND CEIL COS COSECANT COSH COTANGENT DIVQUOT DIVREM EXP FABS FLOOR FMOD FREPM FREXPN LOG LOG10 MAX MAXN MIN MINNZ MODF POW ROUND ROUND2VALUE ROUNDINT SECANT SIN SINH SQROOT TAN TANH FLOAT2STRING FLOAT2STRINGNC INSTR LEFT LEN LTRIM MID RIGHT RTRIM STRING STRINGNC TOLOWER TOUPPER TRIM NUMDAYS READ_DATE STAGING",built_in:"IDENTIFIER OPTIONS XML_ELEMENT XML_OP XML_ELEMENT_OF DOMDOCCREATE DOMDOCLOADFILE DOMDOCLOADXML DOMDOCSAVEFILE DOMDOCGETROOT DOMDOCADDPI DOMNODEGETNAME DOMNODEGETTYPE DOMNODEGETVALUE DOMNODEGETCHILDCT DOMNODEGETFIRSTCHILD DOMNODEGETSIBLING DOMNODECREATECHILDELEMENT DOMNODESETATTRIBUTE DOMNODEGETCHILDELEMENTCT DOMNODEGETFIRSTCHILDELEMENT DOMNODEGETSIBLINGELEMENT DOMNODEGETATTRIBUTECT DOMNODEGETATTRIBUTEI DOMNODEGETATTRIBUTEBYNAME DOMNODEGETBYNAME"},c:[a.CLCM,a.CBCM,a.ASM,a.QSM,a.CNM,{cN:"array",b:"#[a-zA-Z .]+"}]}});hljs.registerLanguage("rib",function(a){return{k:"ArchiveRecord AreaLightSource Atmosphere Attribute AttributeBegin AttributeEnd Basis Begin Blobby Bound Clipping ClippingPlane Color ColorSamples ConcatTransform Cone CoordinateSystem CoordSysTransform CropWindow Curves Cylinder DepthOfField Detail DetailRange Disk Displacement Display End ErrorHandler Exposure Exterior Format FrameAspectRatio FrameBegin FrameEnd GeneralPolygon GeometricApproximation Geometry Hider Hyperboloid Identity Illuminate Imager Interior LightSource MakeCubeFaceEnvironment MakeLatLongEnvironment MakeShadow MakeTexture Matte MotionBegin MotionEnd NuPatch ObjectBegin ObjectEnd ObjectInstance Opacity Option Orientation Paraboloid Patch PatchMesh Perspective PixelFilter PixelSamples PixelVariance Points PointsGeneralPolygons PointsPolygons Polygon Procedural Projection Quantize ReadArchive RelativeDetail ReverseOrientation Rotate Scale ScreenWindow ShadingInterpolation ShadingRate Shutter Sides Skew SolidBegin SolidEnd Sphere SubdivisionMesh Surface TextureCoordinates Torus Transform TransformBegin TransformEnd TransformPoints Translate TrimCurve WorldBegin WorldEnd",i:"",c:[a.HCM,a.CNM,a.ASM,a.QSM]}});hljs.registerLanguage("diff",function(a){return{aliases:["patch"],c:[{cN:"chunk",r:10,v:[{b:/^\@\@ +\-\d+,\d+ +\+\d+,\d+ +\@\@$/},{b:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{b:/^\-\-\- +\d+,\d+ +\-\-\-\-$/}]},{cN:"header",v:[{b:/Index: /,e:/$/},{b:/=====/,e:/=====$/},{b:/^\-\-\-/,e:/$/},{b:/^\*{3} /,e:/$/},{b:/^\+\+\+/,e:/$/},{b:/\*{5}/,e:/\*{5}$/}]},{cN:"addition",b:"^\\+",e:"$"},{cN:"deletion",b:"^\\-",e:"$"},{cN:"change",b:"^\\!",e:"$"}]}});hljs.registerLanguage("markdown",function(a){return{aliases:["md","mkdown","mkd"],c:[{cN:"header",v:[{b:"^#{1,6}",e:"$"},{b:"^.+?\\n[=-]{2,}$"}]},{b:"<",e:">",sL:"xml",r:0},{cN:"bullet",b:"^([*+-]|(\\d+\\.))\\s+"},{cN:"strong",b:"[*_]{2}.+?[*_]{2}"},{cN:"emphasis",v:[{b:"\\*.+?\\*"},{b:"_.+?_",r:0}]},{cN:"blockquote",b:"^>\\s+",e:"$"},{cN:"code",v:[{b:"`.+?`"},{b:"^( {4}|\t)",e:"$",r:0}]},{cN:"horizontal_rule",b:"^[-\\*]{3,}",e:"$"},{b:"\\[.+?\\][\\(\\[].*?[\\)\\]]",rB:true,c:[{cN:"link_label",b:"\\[",e:"\\]",eB:true,rE:true,r:0},{cN:"link_url",b:"\\]\\(",e:"\\)",eB:true,eE:true},{cN:"link_reference",b:"\\]\\[",e:"\\]",eB:true,eE:true}],r:10},{b:"^\\[.+\\]:",rB:true,c:[{cN:"link_reference",b:"\\[",e:"\\]:",eB:true,eE:true,starts:{cN:"link_url",e:"$"}}]}]}});hljs.registerLanguage("dart",function(b){var d={cN:"subst",b:"\\$\\{",e:"}",k:"true false null this is new super"};var c={cN:"string",v:[{b:"r'''",e:"'''"},{b:'r"""',e:'"""'},{b:"r'",e:"'",i:"\\n"},{b:'r"',e:'"',i:"\\n"},{b:"'''",e:"'''",c:[b.BE,d]},{b:'"""',e:'"""',c:[b.BE,d]},{b:"'",e:"'",i:"\\n",c:[b.BE,d]},{b:'"',e:'"',i:"\\n",c:[b.BE,d]}]};d.c=[b.CNM,c];var a={keyword:"assert break case catch class const continue default do else enum extends false final finally for if in is new null rethrow return super switch this throw true try var void while with",literal:"abstract as dynamic export external factory get implements import library operator part set static typedef",built_in:"print Comparable DateTime Duration Function Iterable Iterator List Map Match Null Object Pattern RegExp Set Stopwatch String StringBuffer StringSink Symbol Type Uri bool double int num document window querySelector querySelectorAll Element ElementList"};return{k:a,c:[c,{cN:"dartdoc",b:"/\\*\\*",e:"\\*/",sL:"markdown",subLanguageMode:"continuous"},{cN:"dartdoc",b:"///",e:"$",sL:"markdown",subLanguageMode:"continuous"},b.CLCM,b.CBCM,{cN:"class",bK:"class interface",e:"{",eE:true,c:[{bK:"extends implements"},b.UTM]},b.CNM,{cN:"annotation",b:"@[A-Za-z]+"},{b:"=>"}]}});hljs.registerLanguage("haml",function(a){return{cI:true,c:[{cN:"doctype",b:"^!!!( (5|1\\.1|Strict|Frameset|Basic|Mobile|RDFa|XML\\b.*))?$",r:10},{cN:"comment",b:"^\\s*(!=#|=#|-#|/).*$",r:0},{b:"^\\s*(-|=|!=)(?!#)",starts:{e:"\\n",sL:"ruby"}},{cN:"tag",b:"^\\s*%",c:[{cN:"title",b:"\\w+"},{cN:"value",b:"[#\\.]\\w+"},{b:"{\\s*",e:"\\s*}",eE:true,c:[{b:":\\w+\\s*=>",e:",\\s+",rB:true,eW:true,c:[{cN:"symbol",b:":\\w+"},{cN:"string",b:'"',e:'"'},{cN:"string",b:"'",e:"'"},{b:"\\w+",r:0}]}]},{b:"\\(\\s*",e:"\\s*\\)",eE:true,c:[{b:"\\w+\\s*=",e:"\\s+",rB:true,eW:true,c:[{cN:"attribute",b:"\\w+",r:0},{cN:"string",b:'"',e:'"'},{cN:"string",b:"'",e:"'"},{b:"\\w+",r:0}]}]}]},{cN:"bullet",b:"^\\s*[=~]\\s*",r:0},{b:"#{",starts:{e:"}",sL:"ruby"}}]}});hljs.registerLanguage("javascript",function(a){return{aliases:["js"],k:{keyword:"in if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const class",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document"},c:[{cN:"pi",b:/^\s*('|")use strict('|")/,r:10},a.ASM,a.QSM,a.CLCM,a.CBCM,a.CNM,{b:"("+a.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[a.CLCM,a.CBCM,a.RM,{b:/,e:/>;/,r:0,sL:"xml"}],r:0},{cN:"function",bK:"function",e:/\{/,eE:true,c:[a.inherit(a.TM,{b:/[A-Za-z$_][0-9A-Za-z$_]*/}),{cN:"params",b:/\(/,e:/\)/,c:[a.CLCM,a.CBCM],i:/["'\(]/}],i:/\[|%/},{b:/\$[(.]/},{b:"\\."+a.IR,r:0}]}});hljs.registerLanguage("xml",function(a){var c="[A-Za-z0-9\\._:-]+";var d={b:/<\?(php)?(?!\w)/,e:/\?>/,sL:"php",subLanguageMode:"continuous"};var b={eW:true,i:/,r:0,c:[d,{cN:"attribute",b:c,r:0},{b:"=",r:0,c:[{cN:"value",v:[{b:/"/,e:/"/},{b:/'/,e:/'/},{b:/[^\s\/>]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xsl","plist"],cI:true,c:[{cN:"doctype",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"",rE:true,sL:"css"}},{cN:"tag",b:"
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/presentation/plugin/markdown/example.md b/presentation/plugin/markdown/example.md
deleted file mode 100644
index 6f6f577a1..000000000
--- a/presentation/plugin/markdown/example.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# Markdown Demo
-
-
-
-## External 1.1
-
-Content 1.1
-
-Note: This will only appear in the speaker notes window.
-
-
-## External 1.2
-
-Content 1.2
-
-
-
-## External 2
-
-Content 2.1
-
-
-
-## External 3.1
-
-Content 3.1
-
-
-## External 3.2
-
-Content 3.2
diff --git a/presentation/plugin/markdown/markdown.js b/presentation/plugin/markdown/markdown.js
deleted file mode 100755
index 9afee065f..000000000
--- a/presentation/plugin/markdown/markdown.js
+++ /dev/null
@@ -1,393 +0,0 @@
-/**
- * The reveal.js markdown plugin. Handles parsing of
- * markdown inside of presentations as well as loading
- * of external markdown documents.
- */
-(function( root, factory ) {
- if( typeof exports === 'object' ) {
- module.exports = factory( require( './marked' ) );
- }
- else {
- // Browser globals (root is window)
- root.RevealMarkdown = factory( root.marked );
- root.RevealMarkdown.initialize();
- }
-}( this, function( marked ) {
-
- if( typeof marked === 'undefined' ) {
- throw 'The reveal.js Markdown plugin requires marked to be loaded';
- }
-
- if( typeof hljs !== 'undefined' ) {
- marked.setOptions({
- highlight: function( lang, code ) {
- return hljs.highlightAuto( lang, code ).value;
- }
- });
- }
-
- var DEFAULT_SLIDE_SEPARATOR = '^\n---\n$',
- DEFAULT_NOTES_SEPARATOR = 'note:',
- DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR = '\\\.element\\\s*?(.+?)$',
- DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR = '\\\.slide:\\\s*?(\\\S.+?)$';
-
-
- /**
- * Retrieves the markdown contents of a slide section
- * element. Normalizes leading tabs/whitespace.
- */
- function getMarkdownFromSlide( section ) {
-
- var template = section.querySelector( 'script' );
-
- // strip leading whitespace so it isn't evaluated as code
- var text = ( template || section ).textContent;
-
- var leadingWs = text.match( /^\n?(\s*)/ )[1].length,
- leadingTabs = text.match( /^\n?(\t*)/ )[1].length;
-
- if( leadingTabs > 0 ) {
- text = text.replace( new RegExp('\\n?\\t{' + leadingTabs + '}','g'), '\n' );
- }
- else if( leadingWs > 1 ) {
- text = text.replace( new RegExp('\\n? {' + leadingWs + '}'), '\n' );
- }
-
- return text;
-
- }
-
- /**
- * Given a markdown slide section element, this will
- * return all arguments that aren't related to markdown
- * parsing. Used to forward any other user-defined arguments
- * to the output markdown slide.
- */
- function getForwardedAttributes( section ) {
-
- var attributes = section.attributes;
- var result = [];
-
- for( var i = 0, len = attributes.length; i < len; i++ ) {
- var name = attributes[i].name,
- value = attributes[i].value;
-
- // disregard attributes that are used for markdown loading/parsing
- if( /data\-(markdown|separator|vertical|notes)/gi.test( name ) ) continue;
-
- if( value ) {
- result.push( name + '=' + value );
- }
- else {
- result.push( name );
- }
- }
-
- return result.join( ' ' );
-
- }
-
- /**
- * Inspects the given options and fills out default
- * values for what's not defined.
- */
- function getSlidifyOptions( options ) {
-
- options = options || {};
- options.separator = options.separator || DEFAULT_SLIDE_SEPARATOR;
- options.notesSeparator = options.notesSeparator || DEFAULT_NOTES_SEPARATOR;
- options.attributes = options.attributes || '';
-
- return options;
-
- }
-
- /**
- * Helper function for constructing a markdown slide.
- */
- function createMarkdownSlide( content, options ) {
-
- options = getSlidifyOptions( options );
-
- var notesMatch = content.split( new RegExp( options.notesSeparator, 'mgi' ) );
-
- if( notesMatch.length === 2 ) {
- content = notesMatch[0] + '' + notesMatch[1].trim() + ' ';
- }
-
- return '';
-
- }
-
- /**
- * Parses a data string into multiple slides based
- * on the passed in separator arguments.
- */
- function slidify( markdown, options ) {
-
- options = getSlidifyOptions( options );
-
- var separatorRegex = new RegExp( options.separator + ( options.verticalSeparator ? '|' + options.verticalSeparator : '' ), 'mg' ),
- horizontalSeparatorRegex = new RegExp( options.separator );
-
- var matches,
- lastIndex = 0,
- isHorizontal,
- wasHorizontal = true,
- content,
- sectionStack = [];
-
- // iterate until all blocks between separators are stacked up
- while( matches = separatorRegex.exec( markdown ) ) {
- notes = null;
-
- // determine direction (horizontal by default)
- isHorizontal = horizontalSeparatorRegex.test( matches[0] );
-
- if( !isHorizontal && wasHorizontal ) {
- // create vertical stack
- sectionStack.push( [] );
- }
-
- // pluck slide content from markdown input
- content = markdown.substring( lastIndex, matches.index );
-
- if( isHorizontal && wasHorizontal ) {
- // add to horizontal stack
- sectionStack.push( content );
- }
- else {
- // add to vertical stack
- sectionStack[sectionStack.length-1].push( content );
- }
-
- lastIndex = separatorRegex.lastIndex;
- wasHorizontal = isHorizontal;
- }
-
- // add the remaining slide
- ( wasHorizontal ? sectionStack : sectionStack[sectionStack.length-1] ).push( markdown.substring( lastIndex ) );
-
- var markdownSections = '';
-
- // flatten the hierarchical stack, and insert tags
- for( var i = 0, len = sectionStack.length; i < len; i++ ) {
- // vertical
- if( sectionStack[i] instanceof Array ) {
- markdownSections += '';
-
- sectionStack[i].forEach( function( child ) {
- markdownSections += '' + createMarkdownSlide( child, options ) + ' ';
- } );
-
- markdownSections += ' ';
- }
- else {
- markdownSections += '' + createMarkdownSlide( sectionStack[i], options ) + ' ';
- }
- }
-
- return markdownSections;
-
- }
-
- /**
- * Parses any current data-markdown slides, splits
- * multi-slide markdown into separate sections and
- * handles loading of external markdown.
- */
- function processSlides() {
-
- var sections = document.querySelectorAll( '[data-markdown]'),
- section;
-
- for( var i = 0, len = sections.length; i < len; i++ ) {
-
- section = sections[i];
-
- if( section.getAttribute( 'data-markdown' ).length ) {
-
- var xhr = new XMLHttpRequest(),
- url = section.getAttribute( 'data-markdown' );
-
- datacharset = section.getAttribute( 'data-charset' );
-
- // see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes
- if( datacharset != null && datacharset != '' ) {
- xhr.overrideMimeType( 'text/html; charset=' + datacharset );
- }
-
- xhr.onreadystatechange = function() {
- if( xhr.readyState === 4 ) {
- // file protocol yields status code 0 (useful for local debug, mobile applications etc.)
- if ( ( xhr.status >= 200 && xhr.status < 300 ) || xhr.status === 0 ) {
-
- section.outerHTML = slidify( xhr.responseText, {
- separator: section.getAttribute( 'data-separator' ),
- verticalSeparator: section.getAttribute( 'data-separator-vertical' ),
- notesSeparator: section.getAttribute( 'data-separator-notes' ),
- attributes: getForwardedAttributes( section )
- });
-
- }
- else {
-
- section.outerHTML = '' +
- 'ERROR: The attempt to fetch ' + url + ' failed with HTTP status ' + xhr.status + '.' +
- 'Check your browser\'s JavaScript console for more details.' +
- 'Remember that you need to serve the presentation HTML from a HTTP server.
' +
- ' ';
-
- }
- }
- };
-
- xhr.open( 'GET', url, false );
-
- try {
- xhr.send();
- }
- catch ( e ) {
- alert( 'Failed to get the Markdown file ' + url + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + e );
- }
-
- }
- else if( section.getAttribute( 'data-separator' ) || section.getAttribute( 'data-separator-vertical' ) || section.getAttribute( 'data-separator-notes' ) ) {
-
- section.outerHTML = slidify( getMarkdownFromSlide( section ), {
- separator: section.getAttribute( 'data-separator' ),
- verticalSeparator: section.getAttribute( 'data-separator-vertical' ),
- notesSeparator: section.getAttribute( 'data-separator-notes' ),
- attributes: getForwardedAttributes( section )
- });
-
- }
- else {
- section.innerHTML = createMarkdownSlide( getMarkdownFromSlide( section ) );
- }
- }
-
- }
-
- /**
- * Check if a node value has the attributes pattern.
- * If yes, extract it and add that value as one or several attributes
- * the the terget element.
- *
- * You need Cache Killer on Chrome to see the effect on any FOM transformation
- * directly on refresh (F5)
- * http://stackoverflow.com/questions/5690269/disabling-chrome-cache-for-website-development/7000899#answer-11786277
- */
- function addAttributeInElement( node, elementTarget, separator ) {
-
- var mardownClassesInElementsRegex = new RegExp( separator, 'mg' );
- var mardownClassRegex = new RegExp( "([^\"= ]+?)=\"([^\"=]+?)\"", 'mg' );
- var nodeValue = node.nodeValue;
- if( matches = mardownClassesInElementsRegex.exec( nodeValue ) ) {
-
- var classes = matches[1];
- nodeValue = nodeValue.substring( 0, matches.index ) + nodeValue.substring( mardownClassesInElementsRegex.lastIndex );
- node.nodeValue = nodeValue;
- while( matchesClass = mardownClassRegex.exec( classes ) ) {
- elementTarget.setAttribute( matchesClass[1], matchesClass[2] );
- }
- return true;
- }
- return false;
- }
-
- /**
- * Add attributes to the parent element of a text node,
- * or the element of an attribute node.
- */
- function addAttributes( section, element, previousElement, separatorElementAttributes, separatorSectionAttributes ) {
-
- if ( element != null && element.childNodes != undefined && element.childNodes.length > 0 ) {
- previousParentElement = element;
- for( var i = 0; i < element.childNodes.length; i++ ) {
- childElement = element.childNodes[i];
- if ( i > 0 ) {
- j = i - 1;
- while ( j >= 0 ) {
- aPreviousChildElement = element.childNodes[j];
- if ( typeof aPreviousChildElement.setAttribute == 'function' && aPreviousChildElement.tagName != "BR" ) {
- previousParentElement = aPreviousChildElement;
- break;
- }
- j = j - 1;
- }
- }
- parentSection = section;
- if( childElement.nodeName == "section" ) {
- parentSection = childElement ;
- previousParentElement = childElement ;
- }
- if ( typeof childElement.setAttribute == 'function' || childElement.nodeType == Node.COMMENT_NODE ) {
- addAttributes( parentSection, childElement, previousParentElement, separatorElementAttributes, separatorSectionAttributes );
- }
- }
- }
-
- if ( element.nodeType == Node.COMMENT_NODE ) {
- if ( addAttributeInElement( element, previousElement, separatorElementAttributes ) == false ) {
- addAttributeInElement( element, section, separatorSectionAttributes );
- }
- }
- }
-
- /**
- * Converts any current data-markdown slides in the
- * DOM to HTML.
- */
- function convertSlides() {
-
- var sections = document.querySelectorAll( '[data-markdown]');
-
- for( var i = 0, len = sections.length; i < len; i++ ) {
-
- var section = sections[i];
-
- // Only parse the same slide once
- if( !section.getAttribute( 'data-markdown-parsed' ) ) {
-
- section.setAttribute( 'data-markdown-parsed', true )
-
- var notes = section.querySelector( 'aside.notes' );
- var markdown = getMarkdownFromSlide( section );
-
- section.innerHTML = marked( markdown );
- addAttributes( section, section, null, section.getAttribute( 'data-element-attributes' ) ||
- section.parentNode.getAttribute( 'data-element-attributes' ) ||
- DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR,
- section.getAttribute( 'data-attributes' ) ||
- section.parentNode.getAttribute( 'data-attributes' ) ||
- DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR);
-
- // If there were notes, we need to re-add them after
- // having overwritten the section's HTML
- if( notes ) {
- section.appendChild( notes );
- }
-
- }
-
- }
-
- }
-
- // API
- return {
-
- initialize: function() {
- processSlides();
- convertSlides();
- },
-
- // TODO: Do these belong in the API?
- processSlides: processSlides,
- convertSlides: convertSlides,
- slidify: slidify
-
- };
-
-}));
diff --git a/presentation/plugin/markdown/marked.js b/presentation/plugin/markdown/marked.js
deleted file mode 100644
index ca558fb48..000000000
--- a/presentation/plugin/markdown/marked.js
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * marked - a markdown parser
- * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed)
- * https://github.com/chjj/marked
- */
-
-(function(){var block={newline:/^\n+/,code:/^( {4}[^\n]+\n*)+/,fences:noop,hr:/^( *[-*_]){3,} *(?:\n+|$)/,heading:/^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,nptable:noop,lheading:/^([^\n]+)\n *(=|-){3,} *\n*/,blockquote:/^( *>[^\n]+(\n[^\n]+)*\n*)+/,list:/^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,html:/^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,def:/^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,table:noop,paragraph:/^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
-text:/^[^\n]+/};block.bullet=/(?:[*+-]|\d+\.)/;block.item=/^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;block.item=replace(block.item,"gm")(/bull/g,block.bullet)();block.list=replace(block.list)(/bull/g,block.bullet)("hr",/\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/)();block._tag="(?!(?:"+"a|em|strong|small|s|cite|q|dfn|abbr|data|time|code"+"|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo"+"|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|@)\\b";block.html=replace(block.html)("comment",/\x3c!--[\s\S]*?--\x3e/)("closed",
-/<(tag)[\s\S]+?<\/\1>/)("closing",/])*?>/)(/tag/g,block._tag)();block.paragraph=replace(block.paragraph)("hr",block.hr)("heading",block.heading)("lheading",block.lheading)("blockquote",block.blockquote)("tag","<"+block._tag)("def",block.def)();block.normal=merge({},block);block.gfm=merge({},block.normal,{fences:/^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,paragraph:/^/});block.gfm.paragraph=replace(block.paragraph)("(?!","(?!"+block.gfm.fences.source.replace("\\1",
-"\\2")+"|")();block.tables=merge({},block.gfm,{nptable:/^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,table:/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/});function Lexer(options){this.tokens=[];this.tokens.links={};this.options=options||marked.defaults;this.rules=block.normal;if(this.options.gfm)if(this.options.tables)this.rules=block.tables;else this.rules=block.gfm}Lexer.rules=block;Lexer.lex=function(src,options){var lexer=new Lexer(options);return lexer.lex(src)};
-Lexer.prototype.lex=function(src){src=src.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n");return this.token(src,true)};Lexer.prototype.token=function(src,top){var src=src.replace(/^ +$/gm,""),next,loose,cap,bull,b,item,space,i,l;while(src){if(cap=this.rules.newline.exec(src)){src=src.substring(cap[0].length);if(cap[0].length>1)this.tokens.push({type:"space"})}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);cap=cap[0].replace(/^ {4}/gm,
-"");this.tokens.push({type:"code",text:!this.options.pedantic?cap.replace(/\n+$/,""):cap});continue}if(cap=this.rules.fences.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"code",lang:cap[2],text:cap[3]});continue}if(cap=this.rules.heading.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:"heading",depth:cap[1].length,text:cap[2]});continue}if(top&&(cap=this.rules.nptable.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,
-"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/\n$/,"").split("\n")};for(i=0;i ?/gm,"");this.token(cap,top);this.tokens.push({type:"blockquote_end"});continue}if(cap=this.rules.list.exec(src)){src=src.substring(cap[0].length);
-bull=cap[2];this.tokens.push({type:"list_start",ordered:bull.length>1});cap=cap[0].match(this.rules.item);next=false;l=cap.length;i=0;for(;i1&&b.length>1)){src=cap.slice(i+
-1).join("\n")+src;i=l-1}}loose=next||/\n\n(?!\s*$)/.test(item);if(i!==l-1){next=item[item.length-1]==="\n";if(!loose)loose=next}this.tokens.push({type:loose?"loose_item_start":"list_item_start"});this.token(item,false);this.tokens.push({type:"list_item_end"})}this.tokens.push({type:"list_end"});continue}if(cap=this.rules.html.exec(src)){src=src.substring(cap[0].length);this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:cap[1]==="pre"||cap[1]==="script",text:cap[0]});continue}if(top&&
-(cap=this.rules.def.exec(src))){src=src.substring(cap[0].length);this.tokens.links[cap[1].toLowerCase()]={href:cap[2],title:cap[3]};continue}if(top&&(cap=this.rules.table.exec(src))){src=src.substring(cap[0].length);item={type:"table",header:cap[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:cap[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:cap[3].replace(/(?: *\| *)?\n$/,"").split("\n")};for(i=0;i])/,autolink:/^<([^ >]+(@|:\/)[^ >]+)>/,url:noop,tag:/^\x3c!--[\s\S]*?--\x3e|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
-code:/^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:noop,text:/^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/;inline.link=replace(inline.link)("inside",inline._inside)("href",inline._href)();inline.reflink=replace(inline.reflink)("inside",inline._inside)();inline.normal=merge({},inline);inline.pedantic=merge({},inline.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
-em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/});inline.gfm=merge({},inline.normal,{escape:replace(inline.escape)("])","~|])")(),url:/^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:replace(inline.text)("]|","~]|")("|","|https?://|")()});inline.breaks=merge({},inline.gfm,{br:replace(inline.br)("{2,}","*")(),text:replace(inline.gfm.text)("{2,}","*")()});function InlineLexer(links,options){this.options=options||marked.defaults;this.links=links;this.rules=inline.normal;
-if(!this.links)throw new Error("Tokens array requires a `links` property.");if(this.options.gfm)if(this.options.breaks)this.rules=inline.breaks;else this.rules=inline.gfm;else if(this.options.pedantic)this.rules=inline.pedantic}InlineLexer.rules=inline;InlineLexer.output=function(src,links,options){var inline=new InlineLexer(links,options);return inline.output(src)};InlineLexer.prototype.output=function(src){var out="",link,text,href,cap;while(src){if(cap=this.rules.escape.exec(src)){src=src.substring(cap[0].length);
-out+=cap[1];continue}if(cap=this.rules.autolink.exec(src)){src=src.substring(cap[0].length);if(cap[2]==="@"){text=cap[1][6]===":"?this.mangle(cap[1].substring(7)):this.mangle(cap[1]);href=this.mangle("mailto:")+text}else{text=escape(cap[1]);href=text}out+=''+text+" ";continue}if(cap=this.rules.url.exec(src)){src=src.substring(cap[0].length);text=escape(cap[1]);href=text;out+=''+text+" ";continue}if(cap=this.rules.tag.exec(src)){src=src.substring(cap[0].length);
-out+=this.options.sanitize?escape(cap[0]):cap[0];continue}if(cap=this.rules.link.exec(src)){src=src.substring(cap[0].length);out+=this.outputLink(cap,{href:cap[2],title:cap[3]});continue}if((cap=this.rules.reflink.exec(src))||(cap=this.rules.nolink.exec(src))){src=src.substring(cap[0].length);link=(cap[2]||cap[1]).replace(/\s+/g," ");link=this.links[link.toLowerCase()];if(!link||!link.href){out+=cap[0][0];src=cap[0].substring(1)+src;continue}out+=this.outputLink(cap,link);continue}if(cap=this.rules.strong.exec(src)){src=
-src.substring(cap[0].length);out+=""+this.output(cap[2]||cap[1])+" ";continue}if(cap=this.rules.em.exec(src)){src=src.substring(cap[0].length);out+=""+this.output(cap[2]||cap[1])+" ";continue}if(cap=this.rules.code.exec(src)){src=src.substring(cap[0].length);out+=""+escape(cap[2],true)+"
";continue}if(cap=this.rules.br.exec(src)){src=src.substring(cap[0].length);out+=" ";continue}if(cap=this.rules.del.exec(src)){src=src.substring(cap[0].length);out+=""+
-this.output(cap[1])+"";continue}if(cap=this.rules.text.exec(src)){src=src.substring(cap[0].length);out+=escape(cap[0]);continue}if(src)throw new Error("Infinite loop on byte: "+src.charCodeAt(0));}return out};InlineLexer.prototype.outputLink=function(cap,link){if(cap[0][0]!=="!")return'"+this.output(cap[1])+" ";else return' "};InlineLexer.prototype.smartypants=function(text){if(!this.options.smartypants)return text;return text.replace(/--/g,"\u2014").replace(/'([^']*)'/g,"\u2018$1\u2019").replace(/"([^"]*)"/g,"\u201c$1\u201d").replace(/\.{3}/g,"\u2026")};InlineLexer.prototype.mangle=function(text){var out="",l=text.length,i=0,ch;for(;i0.5)ch="x"+ch.toString(16);out+=""+ch+";"}return out};function Parser(options){this.tokens=[];this.token=null;
-this.options=options||marked.defaults}Parser.parse=function(src,options){var parser=new Parser(options);return parser.parse(src)};Parser.prototype.parse=function(src){this.inline=new InlineLexer(src.links,this.options);this.tokens=src.reverse();var out="";while(this.next())out+=this.tok();return out};Parser.prototype.next=function(){return this.token=this.tokens.pop()};Parser.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0};Parser.prototype.parseText=function(){var body=this.token.text;
-while(this.peek().type==="text")body+="\n"+this.next().text;return this.inline.output(body)};Parser.prototype.tok=function(){switch(this.token.type){case "space":return"";case "hr":return" \n";case "heading":return""+this.inline.output(this.token.text)+" \n";case "code":if(this.options.highlight){var code=this.options.highlight(this.token.text,this.token.lang);if(code!=null&&code!==this.token.text){this.token.escaped=true;this.token.text=code}}if(!this.token.escaped)this.token.text=
-escape(this.token.text,true);return""+this.token.text+"
\n";case "table":var body="",heading,i,row,cell,j;body+="\n\n";for(i=0;i'+heading+"\n":""+heading+" \n"}body+=" \n \n";body+="\n";for(i=0;i\n";for(j=0;j'+cell+"\n":""+cell+" \n"}body+="\n"}body+=" \n";return"\n";case "blockquote_start":var body="";while(this.next().type!=="blockquote_end")body+=this.tok();return"\n"+body+" \n";case "list_start":var type=this.token.ordered?"ol":"ul",body="";while(this.next().type!=="list_end")body+=
-this.tok();return"<"+type+">\n"+body+""+type+">\n";case "list_item_start":var body="";while(this.next().type!=="list_item_end")body+=this.token.type==="text"?this.parseText():this.tok();return""+body+" \n";case "loose_item_start":var body="";while(this.next().type!=="list_item_end")body+=this.tok();return""+body+" \n";case "html":return!this.token.pre&&!this.options.pedantic?this.inline.output(this.token.text):this.token.text;case "paragraph":return""+this.inline.output(this.token.text)+
-"
\n";case "text":return""+this.parseText()+"
\n"}};function escape(html,encode){return html.replace(!encode?/&(?!#?\w+;)/g:/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'")}function replace(regex,opt){regex=regex.source;opt=opt||"";return function self(name,val){if(!name)return new RegExp(regex,opt);val=val.source||val;val=val.replace(/(^|[^\[])\^/g,"$1");regex=regex.replace(name,val);return self}}function noop(){}noop.exec=noop;function merge(obj){var i=
-1,target,key;for(;iAn error occured:
"+escape(e.message+"",true)+" ";throw e;}}marked.options=marked.setOptions=function(opt){merge(marked.defaults,opt);return marked};marked.defaults={gfm:true,tables:true,breaks:false,pedantic:false,sanitize:false,smartLists:false,silent:false,highlight:null,langPrefix:""};marked.Parser=Parser;marked.parser=Parser.parse;marked.Lexer=Lexer;marked.lexer=Lexer.lex;marked.InlineLexer=InlineLexer;marked.inlineLexer=InlineLexer.output;
-marked.parse=marked;if(typeof exports==="object")module.exports=marked;else if(typeof define==="function"&&define.amd)define(function(){return marked});else this.marked=marked}).call(function(){return this||(typeof window!=="undefined"?window:global)}());
diff --git a/presentation/plugin/math/math.js b/presentation/plugin/math/math.js
deleted file mode 100755
index d55d9d1a7..000000000
--- a/presentation/plugin/math/math.js
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * A plugin which enables rendering of math equations inside
- * of reveal.js slides. Essentially a thin wrapper for MathJax.
- *
- * @author Hakim El Hattab
- */
-var RevealMath = window.RevealMath || (function(){
-
- var options = Reveal.getConfig().math || {};
- options.mathjax = options.mathjax || 'http://cdn.mathjax.org/mathjax/latest/MathJax.js';
- options.config = options.config || 'TeX-AMS_HTML-full';
-
- loadScript( options.mathjax + '?config=' + options.config, function() {
-
- MathJax.Hub.Config({
- messageStyle: 'none',
- tex2jax: { inlineMath: [['$','$'],['\\(','\\)']] },
- skipStartupTypeset: true
- });
-
- // Typeset followed by an immediate reveal.js layout since
- // the typesetting process could affect slide height
- MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub ] );
- MathJax.Hub.Queue( Reveal.layout );
-
- // Reprocess equations in slides when they turn visible
- Reveal.addEventListener( 'slidechanged', function( event ) {
-
- MathJax.Hub.Queue( [ 'Typeset', MathJax.Hub, event.currentSlide ] );
-
- } );
-
- } );
-
- function loadScript( url, callback ) {
-
- var head = document.querySelector( 'head' );
- var script = document.createElement( 'script' );
- script.type = 'text/javascript';
- script.src = url;
-
- // Wrapper for callback to make sure it only fires once
- var finish = function() {
- if( typeof callback === 'function' ) {
- callback.call();
- callback = null;
- }
- }
-
- script.onload = finish;
-
- // IE
- script.onreadystatechange = function() {
- if ( this.readyState === 'loaded' ) {
- finish();
- }
- }
-
- // Normal browsers
- head.appendChild( script );
-
- }
-
-})();
diff --git a/presentation/plugin/multiplex/client.js b/presentation/plugin/multiplex/client.js
deleted file mode 100644
index e6179f6de..000000000
--- a/presentation/plugin/multiplex/client.js
+++ /dev/null
@@ -1,13 +0,0 @@
-(function() {
- var multiplex = Reveal.getConfig().multiplex;
- var socketId = multiplex.id;
- var socket = io.connect(multiplex.url);
-
- socket.on(multiplex.id, function(data) {
- // ignore data from sockets that aren't ours
- if (data.socketId !== socketId) { return; }
- if( window.location.host === 'localhost:1947' ) return;
-
- Reveal.slide(data.indexh, data.indexv, data.indexf, 'remote');
- });
-}());
diff --git a/presentation/plugin/multiplex/index.js b/presentation/plugin/multiplex/index.js
deleted file mode 100644
index 6f5d8b112..000000000
--- a/presentation/plugin/multiplex/index.js
+++ /dev/null
@@ -1,56 +0,0 @@
-var express = require('express');
-var fs = require('fs');
-var io = require('socket.io');
-var crypto = require('crypto');
-
-var app = express.createServer();
-var staticDir = express.static;
-
-io = io.listen(app);
-
-var opts = {
- port: 1948,
- baseDir : __dirname + '/../../'
-};
-
-io.sockets.on('connection', function(socket) {
- socket.on('slidechanged', function(slideData) {
- if (typeof slideData.secret == 'undefined' || slideData.secret == null || slideData.secret === '') return;
- if (createHash(slideData.secret) === slideData.socketId) {
- slideData.secret = null;
- socket.broadcast.emit(slideData.socketId, slideData);
- };
- });
-});
-
-app.configure(function() {
- [ 'css', 'js', 'plugin', 'lib' ].forEach(function(dir) {
- app.use('/' + dir, staticDir(opts.baseDir + dir));
- });
-});
-
-app.get("/", function(req, res) {
- res.writeHead(200, {'Content-Type': 'text/html'});
- fs.createReadStream(opts.baseDir + '/index.html').pipe(res);
-});
-
-app.get("/token", function(req,res) {
- var ts = new Date().getTime();
- var rand = Math.floor(Math.random()*9999999);
- var secret = ts.toString() + rand.toString();
- res.send({secret: secret, socketId: createHash(secret)});
-});
-
-var createHash = function(secret) {
- var cipher = crypto.createCipher('blowfish', secret);
- return(cipher.final('hex'));
-};
-
-// Actually listen
-app.listen(opts.port || null);
-
-var brown = '\033[33m',
- green = '\033[32m',
- reset = '\033[0m';
-
-console.log( brown + "reveal.js:" + reset + " Multiplex running on port " + green + opts.port + reset );
\ No newline at end of file
diff --git a/presentation/plugin/multiplex/master.js b/presentation/plugin/multiplex/master.js
deleted file mode 100644
index b6a7eb7dc..000000000
--- a/presentation/plugin/multiplex/master.js
+++ /dev/null
@@ -1,51 +0,0 @@
-(function() {
- // Don't emit events from inside of notes windows
- if ( window.location.search.match( /receiver/gi ) ) { return; }
-
- var multiplex = Reveal.getConfig().multiplex;
-
- var socket = io.connect(multiplex.url);
-
- var notify = function( slideElement, indexh, indexv, origin ) {
- if( typeof origin === 'undefined' && origin !== 'remote' ) {
- var nextindexh;
- var nextindexv;
-
- var fragmentindex = Reveal.getIndices().f;
- if (typeof fragmentindex == 'undefined') {
- fragmentindex = 0;
- }
-
- if (slideElement.nextElementSibling && slideElement.parentNode.nodeName == 'SECTION') {
- nextindexh = indexh;
- nextindexv = indexv + 1;
- } else {
- nextindexh = indexh + 1;
- nextindexv = 0;
- }
-
- var slideData = {
- indexh : indexh,
- indexv : indexv,
- indexf : fragmentindex,
- nextindexh : nextindexh,
- nextindexv : nextindexv,
- secret: multiplex.secret,
- socketId : multiplex.id
- };
-
- socket.emit('slidechanged', slideData);
- }
- }
-
- Reveal.addEventListener( 'slidechanged', function( event ) {
- notify( event.currentSlide, event.indexh, event.indexv, event.origin );
- } );
-
- var fragmentNotify = function( event ) {
- notify( Reveal.getCurrentSlide(), Reveal.getIndices().h, Reveal.getIndices().v, event.origin );
- };
-
- Reveal.addEventListener( 'fragmentshown', fragmentNotify );
- Reveal.addEventListener( 'fragmenthidden', fragmentNotify );
-}());
\ No newline at end of file
diff --git a/presentation/plugin/notes-server/client.js b/presentation/plugin/notes-server/client.js
deleted file mode 100644
index 628586ffb..000000000
--- a/presentation/plugin/notes-server/client.js
+++ /dev/null
@@ -1,60 +0,0 @@
-(function() {
-
- // don't emit events from inside the previews themselves
- if( window.location.search.match( /receiver/gi ) ) { return; }
-
- var socket = io.connect( window.location.origin ),
- socketId = Math.random().toString().slice( 2 );
-
- console.log( 'View slide notes at ' + window.location.origin + '/notes/' + socketId );
-
- window.open( window.location.origin + '/notes/' + socketId, 'notes-' + socketId );
-
- /**
- * Posts the current slide data to the notes window
- */
- function post() {
-
- var slideElement = Reveal.getCurrentSlide(),
- notesElement = slideElement.querySelector( 'aside.notes' );
-
- var messageData = {
- notes: '',
- markdown: false,
- socketId: socketId,
- state: Reveal.getState()
- };
-
- // Look for notes defined in a slide attribute
- if( slideElement.hasAttribute( 'data-notes' ) ) {
- messageData.notes = slideElement.getAttribute( 'data-notes' );
- }
-
- // Look for notes defined in an aside element
- if( notesElement ) {
- messageData.notes = notesElement.innerHTML;
- messageData.markdown = typeof notesElement.getAttribute( 'data-markdown' ) === 'string';
- }
-
- socket.emit( 'statechanged', messageData );
-
- }
-
- // When a new notes window connects, post our current state
- socket.on( 'connect', function( data ) {
- post();
- } );
-
- // Monitor events that trigger a change in state
- Reveal.addEventListener( 'slidechanged', post );
- Reveal.addEventListener( 'fragmentshown', post );
- Reveal.addEventListener( 'fragmenthidden', post );
- Reveal.addEventListener( 'overviewhidden', post );
- Reveal.addEventListener( 'overviewshown', post );
- Reveal.addEventListener( 'paused', post );
- Reveal.addEventListener( 'resumed', post );
-
- // Post the initial state
- post();
-
-}());
diff --git a/presentation/plugin/notes-server/index.js b/presentation/plugin/notes-server/index.js
deleted file mode 100644
index df917f112..000000000
--- a/presentation/plugin/notes-server/index.js
+++ /dev/null
@@ -1,66 +0,0 @@
-var express = require('express');
-var fs = require('fs');
-var io = require('socket.io');
-var _ = require('underscore');
-var Mustache = require('mustache');
-
-var app = express.createServer();
-var staticDir = express.static;
-
-io = io.listen(app);
-
-var opts = {
- port : 1947,
- baseDir : __dirname + '/../../'
-};
-
-io.sockets.on( 'connection', function( socket ) {
-
- socket.on( 'connect', function( data ) {
- socket.broadcast.emit( 'connect', data );
- });
-
- socket.on( 'statechanged', function( data ) {
- socket.broadcast.emit( 'statechanged', data );
- });
-
-});
-
-app.configure( function() {
-
- [ 'css', 'js', 'images', 'plugin', 'lib' ].forEach( function( dir ) {
- app.use( '/' + dir, staticDir( opts.baseDir + dir ) );
- });
-
-});
-
-app.get('/', function( req, res ) {
-
- res.writeHead( 200, { 'Content-Type': 'text/html' } );
- fs.createReadStream( opts.baseDir + '/index.html' ).pipe( res );
-
-});
-
-app.get( '/notes/:socketId', function( req, res ) {
-
- fs.readFile( opts.baseDir + 'plugin/notes-server/notes.html', function( err, data ) {
- res.send( Mustache.to_html( data.toString(), {
- socketId : req.params.socketId
- }));
- });
-
-});
-
-// Actually listen
-app.listen( opts.port || null );
-
-var brown = '\033[33m',
- green = '\033[32m',
- reset = '\033[0m';
-
-var slidesLocation = 'http://localhost' + ( opts.port ? ( ':' + opts.port ) : '' );
-
-console.log( brown + 'reveal.js - Speaker Notes' + reset );
-console.log( '1. Open the slides at ' + green + slidesLocation + reset );
-console.log( '2. Click on the link your JS console to go to the notes page' );
-console.log( '3. Advance through your slides and your notes will advance automatically' );
diff --git a/presentation/plugin/notes-server/notes.html b/presentation/plugin/notes-server/notes.html
deleted file mode 100644
index 72d0317f1..000000000
--- a/presentation/plugin/notes-server/notes.html
+++ /dev/null
@@ -1,396 +0,0 @@
-
-
-
-
-
- reveal.js - Slide Notes
-
-
-
-
-
-
-
- UPCOMING:
-
-
-
Time Click to Reset
-
- 0:00 AM
-
-
- 00 :00 :00
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/presentation/plugin/notes/notes.html b/presentation/plugin/notes/notes.html
deleted file mode 100644
index 0cc8cf612..000000000
--- a/presentation/plugin/notes/notes.html
+++ /dev/null
@@ -1,406 +0,0 @@
-
-
-
-
-
- reveal.js - Slide Notes
-
-
-
-
-
-
-
- UPCOMING:
-
-
-
Time Click to Reset
-
- 0:00 AM
-
-
- 00 :00 :00
-
-
-
-
-
-
-
-
-
-
-
diff --git a/presentation/plugin/notes/notes.js b/presentation/plugin/notes/notes.js
deleted file mode 100644
index 27199af87..000000000
--- a/presentation/plugin/notes/notes.js
+++ /dev/null
@@ -1,122 +0,0 @@
-/**
- * Handles opening of and synchronization with the reveal.js
- * notes window.
- *
- * Handshake process:
- * 1. This window posts 'connect' to notes window
- * - Includes URL of presentation to show
- * 2. Notes window responds with 'connected' when it is available
- * 3. This window proceeds to send the current presentation state
- * to the notes window
- */
-var RevealNotes = (function() {
-
- function openNotes() {
- var jsFileLocation = document.querySelector('script[src$="notes.js"]').src; // this js file path
- jsFileLocation = jsFileLocation.replace(/notes\.js(\?.*)?$/, ''); // the js folder path
- var notesPopup = window.open( jsFileLocation + 'notes.html', 'reveal.js - Notes', 'width=1100,height=700' );
-
- /**
- * Connect to the notes window through a postmessage handshake.
- * Using postmessage enables us to work in situations where the
- * origins differ, such as a presentation being opened from the
- * file system.
- */
- function connect() {
- // Keep trying to connect until we get a 'connected' message back
- var connectInterval = setInterval( function() {
- notesPopup.postMessage( JSON.stringify( {
- namespace: 'reveal-notes',
- type: 'connect',
- url: window.location.protocol + '//' + window.location.host + window.location.pathname,
- state: Reveal.getState()
- } ), '*' );
- }, 500 );
-
- window.addEventListener( 'message', function( event ) {
- var data = JSON.parse( event.data );
- if( data && data.namespace === 'reveal-notes' && data.type === 'connected' ) {
- clearInterval( connectInterval );
- onConnected();
- }
- } );
- }
-
- /**
- * Posts the current slide data to the notes window
- */
- function post() {
-
- var slideElement = Reveal.getCurrentSlide(),
- notesElement = slideElement.querySelector( 'aside.notes' );
-
- var messageData = {
- namespace: 'reveal-notes',
- type: 'state',
- notes: '',
- markdown: false,
- state: Reveal.getState()
- };
-
- // Look for notes defined in a slide attribute
- if( slideElement.hasAttribute( 'data-notes' ) ) {
- messageData.notes = slideElement.getAttribute( 'data-notes' );
- }
-
- // Look for notes defined in an aside element
- if( notesElement ) {
- messageData.notes = notesElement.innerHTML;
- messageData.markdown = typeof notesElement.getAttribute( 'data-markdown' ) === 'string';
- }
-
- notesPopup.postMessage( JSON.stringify( messageData ), '*' );
-
- }
-
- /**
- * Called once we have established a connection to the notes
- * window.
- */
- function onConnected() {
-
- // Monitor events that trigger a change in state
- Reveal.addEventListener( 'slidechanged', post );
- Reveal.addEventListener( 'fragmentshown', post );
- Reveal.addEventListener( 'fragmenthidden', post );
- Reveal.addEventListener( 'overviewhidden', post );
- Reveal.addEventListener( 'overviewshown', post );
- Reveal.addEventListener( 'paused', post );
- Reveal.addEventListener( 'resumed', post );
-
- // Post the initial state
- post();
-
- }
-
- connect();
- }
-
- if( !/receiver/i.test( window.location.search ) ) {
-
- // If the there's a 'notes' query set, open directly
- if( window.location.search.match( /(\?|\&)notes/gi ) !== null ) {
- openNotes();
- }
-
- // Open the notes when the 's' key is hit
- document.addEventListener( 'keydown', function( event ) {
- // Disregard the event if the target is editable or a
- // modifier is present
- if ( document.querySelector( ':focus' ) !== null || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey ) return;
-
- if( event.keyCode === 83 ) {
- event.preventDefault();
- openNotes();
- }
- }, false );
-
- }
-
- return { open: openNotes };
-
-})();
diff --git a/presentation/plugin/print-pdf/print-pdf.js b/presentation/plugin/print-pdf/print-pdf.js
deleted file mode 100644
index 86dc4df8a..000000000
--- a/presentation/plugin/print-pdf/print-pdf.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- * phantomjs script for printing presentations to PDF.
- *
- * Example:
- * phantomjs print-pdf.js "http://lab.hakim.se/reveal-js?print-pdf" reveal-demo.pdf
- *
- * By Manuel Bieh (https://github.com/manuelbieh)
- */
-
-// html2pdf.js
-var page = new WebPage();
-var system = require( 'system' );
-
-var slideWidth = system.args[3] ? system.args[3].split( 'x' )[0] : 960;
-var slideHeight = system.args[3] ? system.args[3].split( 'x' )[1] : 700;
-
-page.viewportSize = {
- width: slideWidth,
- height: slideHeight
-};
-
-// TODO
-// Something is wrong with these config values. An input
-// paper width of 1920px actually results in a 756px wide
-// PDF.
-page.paperSize = {
- width: Math.round( slideWidth * 2 ),
- height: Math.round( slideHeight * 2 ),
- border: 0
-};
-
-var inputFile = system.args[1] || 'index.html?print-pdf';
-var outputFile = system.args[2] || 'slides.pdf';
-
-if( outputFile.match( /\.pdf$/gi ) === null ) {
- outputFile += '.pdf';
-}
-
-console.log( 'Printing PDF (Paper size: '+ page.paperSize.width + 'x' + page.paperSize.height +')' );
-
-page.open( inputFile, function( status ) {
- window.setTimeout( function() {
- console.log( 'Printed succesfully' );
- page.render( outputFile );
- phantom.exit();
- }, 1000 );
-} );
-
diff --git a/presentation/plugin/remotes/remotes.js b/presentation/plugin/remotes/remotes.js
deleted file mode 100644
index ba0dbad7b..000000000
--- a/presentation/plugin/remotes/remotes.js
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- * Touch-based remote controller for your presentation courtesy
- * of the folks at http://remotes.io
- */
-
-(function(window){
-
- /**
- * Detects if we are dealing with a touch enabled device (with some false positives)
- * Borrowed from modernizr: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/touch.js
- */
- var hasTouch = (function(){
- return ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch;
- })();
-
- /**
- * Detects if notes are enable and the current page is opened inside an /iframe
- * this prevents loading Remotes.io several times
- */
- var isNotesAndIframe = (function(){
- return window.RevealNotes && !(self == top);
- })();
-
- if(!hasTouch && !isNotesAndIframe){
- head.ready( 'remotes.ne.min.js', function() {
- new Remotes("preview")
- .on("swipe-left", function(e){ Reveal.right(); })
- .on("swipe-right", function(e){ Reveal.left(); })
- .on("swipe-up", function(e){ Reveal.down(); })
- .on("swipe-down", function(e){ Reveal.up(); })
- .on("tap", function(e){ Reveal.next(); })
- .on("zoom-out", function(e){ Reveal.toggleOverview(true); })
- .on("zoom-in", function(e){ Reveal.toggleOverview(false); })
- ;
- } );
-
- head.js('https://hakim-static.s3.amazonaws.com/reveal-js/remotes.ne.min.js');
- }
-})(window);
\ No newline at end of file
diff --git a/presentation/plugin/search/search.js b/presentation/plugin/search/search.js
deleted file mode 100644
index ae6582e75..000000000
--- a/presentation/plugin/search/search.js
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * Handles finding a text string anywhere in the slides and showing the next occurrence to the user
- * by navigatating to that slide and highlighting it.
- *
- * By Jon Snyder , February 2013
- */
-
-var RevealSearch = (function() {
-
- var matchedSlides;
- var currentMatchedIndex;
- var searchboxDirty;
- var myHilitor;
-
-// Original JavaScript code by Chirp Internet: www.chirp.com.au
-// Please acknowledge use of this code by including this header.
-// 2/2013 jon: modified regex to display any match, not restricted to word boundaries.
-
-function Hilitor(id, tag)
-{
-
- var targetNode = document.getElementById(id) || document.body;
- var hiliteTag = tag || "EM";
- var skipTags = new RegExp("^(?:" + hiliteTag + "|SCRIPT|FORM|SPAN)$");
- var colors = ["#ff6", "#a0ffff", "#9f9", "#f99", "#f6f"];
- var wordColor = [];
- var colorIdx = 0;
- var matchRegex = "";
- var matchingSlides = [];
-
- this.setRegex = function(input)
- {
- input = input.replace(/^[^\w]+|[^\w]+$/g, "").replace(/[^\w'-]+/g, "|");
- matchRegex = new RegExp("(" + input + ")","i");
- }
-
- this.getRegex = function()
- {
- return matchRegex.toString().replace(/^\/\\b\(|\)\\b\/i$/g, "").replace(/\|/g, " ");
- }
-
- // recursively apply word highlighting
- this.hiliteWords = function(node)
- {
- if(node == undefined || !node) return;
- if(!matchRegex) return;
- if(skipTags.test(node.nodeName)) return;
-
- if(node.hasChildNodes()) {
- for(var i=0; i < node.childNodes.length; i++)
- this.hiliteWords(node.childNodes[i]);
- }
- if(node.nodeType == 3) { // NODE_TEXT
- if((nv = node.nodeValue) && (regs = matchRegex.exec(nv))) {
- //find the slide's section element and save it in our list of matching slides
- var secnode = node.parentNode;
- while (secnode.nodeName != 'SECTION') {
- secnode = secnode.parentNode;
- }
-
- var slideIndex = Reveal.getIndices(secnode);
- var slidelen = matchingSlides.length;
- var alreadyAdded = false;
- for (var i=0; i < slidelen; i++) {
- if ( (matchingSlides[i].h === slideIndex.h) && (matchingSlides[i].v === slideIndex.v) ) {
- alreadyAdded = true;
- }
- }
- if (! alreadyAdded) {
- matchingSlides.push(slideIndex);
- }
-
- if(!wordColor[regs[0].toLowerCase()]) {
- wordColor[regs[0].toLowerCase()] = colors[colorIdx++ % colors.length];
- }
-
- var match = document.createElement(hiliteTag);
- match.appendChild(document.createTextNode(regs[0]));
- match.style.backgroundColor = wordColor[regs[0].toLowerCase()];
- match.style.fontStyle = "inherit";
- match.style.color = "#000";
-
- var after = node.splitText(regs.index);
- after.nodeValue = after.nodeValue.substring(regs[0].length);
- node.parentNode.insertBefore(match, after);
- }
- }
- };
-
- // remove highlighting
- this.remove = function()
- {
- var arr = document.getElementsByTagName(hiliteTag);
- while(arr.length && (el = arr[0])) {
- el.parentNode.replaceChild(el.firstChild, el);
- }
- };
-
- // start highlighting at target node
- this.apply = function(input)
- {
- if(input == undefined || !input) return;
- this.remove();
- this.setRegex(input);
- this.hiliteWords(targetNode);
- return matchingSlides;
- };
-
-}
-
- function openSearch() {
- //ensure the search term input dialog is visible and has focus:
- var inputbox = document.getElementById("searchinput");
- inputbox.style.display = "inline";
- inputbox.focus();
- inputbox.select();
- }
-
- function toggleSearch() {
- var inputbox = document.getElementById("searchinput");
- if (inputbox.style.display !== "inline") {
- openSearch();
- }
- else {
- inputbox.style.display = "none";
- myHilitor.remove();
- }
- }
-
- function doSearch() {
- //if there's been a change in the search term, perform a new search:
- if (searchboxDirty) {
- var searchstring = document.getElementById("searchinput").value;
-
- //find the keyword amongst the slides
- myHilitor = new Hilitor("slidecontent");
- matchedSlides = myHilitor.apply(searchstring);
- currentMatchedIndex = 0;
- }
-
- //navigate to the next slide that has the keyword, wrapping to the first if necessary
- if (matchedSlides.length && (matchedSlides.length <= currentMatchedIndex)) {
- currentMatchedIndex = 0;
- }
- if (matchedSlides.length > currentMatchedIndex) {
- Reveal.slide(matchedSlides[currentMatchedIndex].h, matchedSlides[currentMatchedIndex].v);
- currentMatchedIndex++;
- }
- }
-
- var dom = {};
- dom.wrapper = document.querySelector( '.reveal' );
-
- if( !dom.wrapper.querySelector( '.searchbox' ) ) {
- var searchElement = document.createElement( 'div' );
- searchElement.id = "searchinputdiv";
- searchElement.classList.add( 'searchdiv' );
- searchElement.style.position = 'absolute';
- searchElement.style.top = '10px';
- searchElement.style.left = '10px';
- //embedded base64 search icon Designed by Sketchdock - http://www.sketchdock.com/:
- searchElement.innerHTML = ' ';
- dom.wrapper.appendChild( searchElement );
- }
-
- document.getElementById("searchbutton").addEventListener( 'click', function(event) {
- doSearch();
- }, false );
-
- document.getElementById("searchinput").addEventListener( 'keyup', function( event ) {
- switch (event.keyCode) {
- case 13:
- event.preventDefault();
- doSearch();
- searchboxDirty = false;
- break;
- default:
- searchboxDirty = true;
- }
- }, false );
-
- // Open the search when the 's' key is hit (yes, this conflicts with the notes plugin, disabling for now)
- /*
- document.addEventListener( 'keydown', function( event ) {
- // Disregard the event if the target is editable or a
- // modifier is present
- if ( document.querySelector( ':focus' ) !== null || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey ) return;
-
- if( event.keyCode === 83 ) {
- event.preventDefault();
- openSearch();
- }
- }, false );
-*/
- return { open: openSearch };
-})();
diff --git a/presentation/plugin/zoom-js/zoom.js b/presentation/plugin/zoom-js/zoom.js
deleted file mode 100644
index da2c10acb..000000000
--- a/presentation/plugin/zoom-js/zoom.js
+++ /dev/null
@@ -1,278 +0,0 @@
-// Custom reveal.js integration
-(function(){
- var isEnabled = true;
-
- document.querySelector( '.reveal' ).addEventListener( 'mousedown', function( event ) {
- var modifier = ( Reveal.getConfig().zoomKey ? Reveal.getConfig().zoomKey : 'alt' ) + 'Key';
-
- var zoomPadding = 20;
- var revealScale = Reveal.getScale();
-
- if( event[ modifier ] && isEnabled ) {
- event.preventDefault();
-
- var bounds = event.target.getBoundingClientRect();
-
- zoom.to({
- x: ( bounds.left * revealScale ) - zoomPadding,
- y: ( bounds.top * revealScale ) - zoomPadding,
- width: ( bounds.width * revealScale ) + ( zoomPadding * 2 ),
- height: ( bounds.height * revealScale ) + ( zoomPadding * 2 ),
- pan: false
- });
- }
- } );
-
- Reveal.addEventListener( 'overviewshown', function() { isEnabled = false; } );
- Reveal.addEventListener( 'overviewhidden', function() { isEnabled = true; } );
-})();
-
-/*!
- * zoom.js 0.3 (modified for use with reveal.js)
- * http://lab.hakim.se/zoom-js
- * MIT licensed
- *
- * Copyright (C) 2011-2014 Hakim El Hattab, http://hakim.se
- */
-var zoom = (function(){
-
- // The current zoom level (scale)
- var level = 1;
-
- // The current mouse position, used for panning
- var mouseX = 0,
- mouseY = 0;
-
- // Timeout before pan is activated
- var panEngageTimeout = -1,
- panUpdateInterval = -1;
-
- // Check for transform support so that we can fallback otherwise
- var supportsTransforms = 'WebkitTransform' in document.body.style ||
- 'MozTransform' in document.body.style ||
- 'msTransform' in document.body.style ||
- 'OTransform' in document.body.style ||
- 'transform' in document.body.style;
-
- if( supportsTransforms ) {
- // The easing that will be applied when we zoom in/out
- document.body.style.transition = 'transform 0.8s ease';
- document.body.style.OTransition = '-o-transform 0.8s ease';
- document.body.style.msTransition = '-ms-transform 0.8s ease';
- document.body.style.MozTransition = '-moz-transform 0.8s ease';
- document.body.style.WebkitTransition = '-webkit-transform 0.8s ease';
- }
-
- // Zoom out if the user hits escape
- document.addEventListener( 'keyup', function( event ) {
- if( level !== 1 && event.keyCode === 27 ) {
- zoom.out();
- }
- } );
-
- // Monitor mouse movement for panning
- document.addEventListener( 'mousemove', function( event ) {
- if( level !== 1 ) {
- mouseX = event.clientX;
- mouseY = event.clientY;
- }
- } );
-
- /**
- * Applies the CSS required to zoom in, prefers the use of CSS3
- * transforms but falls back on zoom for IE.
- *
- * @param {Object} rect
- * @param {Number} scale
- */
- function magnify( rect, scale ) {
-
- var scrollOffset = getScrollOffset();
-
- // Ensure a width/height is set
- rect.width = rect.width || 1;
- rect.height = rect.height || 1;
-
- // Center the rect within the zoomed viewport
- rect.x -= ( window.innerWidth - ( rect.width * scale ) ) / 2;
- rect.y -= ( window.innerHeight - ( rect.height * scale ) ) / 2;
-
- if( supportsTransforms ) {
- // Reset
- if( scale === 1 ) {
- document.body.style.transform = '';
- document.body.style.OTransform = '';
- document.body.style.msTransform = '';
- document.body.style.MozTransform = '';
- document.body.style.WebkitTransform = '';
- }
- // Scale
- else {
- var origin = scrollOffset.x +'px '+ scrollOffset.y +'px',
- transform = 'translate('+ -rect.x +'px,'+ -rect.y +'px) scale('+ scale +')';
-
- document.body.style.transformOrigin = origin;
- document.body.style.OTransformOrigin = origin;
- document.body.style.msTransformOrigin = origin;
- document.body.style.MozTransformOrigin = origin;
- document.body.style.WebkitTransformOrigin = origin;
-
- document.body.style.transform = transform;
- document.body.style.OTransform = transform;
- document.body.style.msTransform = transform;
- document.body.style.MozTransform = transform;
- document.body.style.WebkitTransform = transform;
- }
- }
- else {
- // Reset
- if( scale === 1 ) {
- document.body.style.position = '';
- document.body.style.left = '';
- document.body.style.top = '';
- document.body.style.width = '';
- document.body.style.height = '';
- document.body.style.zoom = '';
- }
- // Scale
- else {
- document.body.style.position = 'relative';
- document.body.style.left = ( - ( scrollOffset.x + rect.x ) / scale ) + 'px';
- document.body.style.top = ( - ( scrollOffset.y + rect.y ) / scale ) + 'px';
- document.body.style.width = ( scale * 100 ) + '%';
- document.body.style.height = ( scale * 100 ) + '%';
- document.body.style.zoom = scale;
- }
- }
-
- level = scale;
-
- if( document.documentElement.classList ) {
- if( level !== 1 ) {
- document.documentElement.classList.add( 'zoomed' );
- }
- else {
- document.documentElement.classList.remove( 'zoomed' );
- }
- }
- }
-
- /**
- * Pan the document when the mosue cursor approaches the edges
- * of the window.
- */
- function pan() {
- var range = 0.12,
- rangeX = window.innerWidth * range,
- rangeY = window.innerHeight * range,
- scrollOffset = getScrollOffset();
-
- // Up
- if( mouseY < rangeY ) {
- window.scroll( scrollOffset.x, scrollOffset.y - ( 1 - ( mouseY / rangeY ) ) * ( 14 / level ) );
- }
- // Down
- else if( mouseY > window.innerHeight - rangeY ) {
- window.scroll( scrollOffset.x, scrollOffset.y + ( 1 - ( window.innerHeight - mouseY ) / rangeY ) * ( 14 / level ) );
- }
-
- // Left
- if( mouseX < rangeX ) {
- window.scroll( scrollOffset.x - ( 1 - ( mouseX / rangeX ) ) * ( 14 / level ), scrollOffset.y );
- }
- // Right
- else if( mouseX > window.innerWidth - rangeX ) {
- window.scroll( scrollOffset.x + ( 1 - ( window.innerWidth - mouseX ) / rangeX ) * ( 14 / level ), scrollOffset.y );
- }
- }
-
- function getScrollOffset() {
- return {
- x: window.scrollX !== undefined ? window.scrollX : window.pageXOffset,
- y: window.scrollY !== undefined ? window.scrollY : window.pageYOffset
- }
- }
-
- return {
- /**
- * Zooms in on either a rectangle or HTML element.
- *
- * @param {Object} options
- * - element: HTML element to zoom in on
- * OR
- * - x/y: coordinates in non-transformed space to zoom in on
- * - width/height: the portion of the screen to zoom in on
- * - scale: can be used instead of width/height to explicitly set scale
- */
- to: function( options ) {
-
- // Due to an implementation limitation we can't zoom in
- // to another element without zooming out first
- if( level !== 1 ) {
- zoom.out();
- }
- else {
- options.x = options.x || 0;
- options.y = options.y || 0;
-
- // If an element is set, that takes precedence
- if( !!options.element ) {
- // Space around the zoomed in element to leave on screen
- var padding = 20;
- var bounds = options.element.getBoundingClientRect();
-
- options.x = bounds.left - padding;
- options.y = bounds.top - padding;
- options.width = bounds.width + ( padding * 2 );
- options.height = bounds.height + ( padding * 2 );
- }
-
- // If width/height values are set, calculate scale from those values
- if( options.width !== undefined && options.height !== undefined ) {
- options.scale = Math.max( Math.min( window.innerWidth / options.width, window.innerHeight / options.height ), 1 );
- }
-
- if( options.scale > 1 ) {
- options.x *= options.scale;
- options.y *= options.scale;
-
- magnify( options, options.scale );
-
- if( options.pan !== false ) {
-
- // Wait with engaging panning as it may conflict with the
- // zoom transition
- panEngageTimeout = setTimeout( function() {
- panUpdateInterval = setInterval( pan, 1000 / 60 );
- }, 800 );
-
- }
- }
- }
- },
-
- /**
- * Resets the document zoom state to its default.
- */
- out: function() {
- clearTimeout( panEngageTimeout );
- clearInterval( panUpdateInterval );
-
- magnify( { x: 0, y: 0 }, 1 );
-
- level = 1;
- },
-
- // Alias
- magnify: function( options ) { this.to( options ) },
- reset: function() { this.out() },
-
- zoomLevel: function() {
- return level;
- }
- }
-
-})();
-
-
-
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index f870ae3a6..000000000
--- a/requirements.txt
+++ /dev/null
@@ -1,80 +0,0 @@
--i https://pypi.python.org/simple
-alabaster==0.7.12
-apipkg==1.5
-atomicwrites==1.3.0
-attrs==19.1.0
-babel==2.7.0
-brotli==1.0.7
-certifi==2019.6.16
-chardet==3.0.4
-coverage==4.5.4
-coveralls==1.5.1
-dateparser==0.7.0
-django-cors-headers==2.4.0
-django-crispy-forms==1.7.2
-django-extensions==2.1.4
-django-filter==2.1.0
-django==2.0.10
-djangoql==0.12.3
-djangorestframework==3.9.1
-docopt==0.6.2
-docutils==0.15.2
-execnet==1.6.1
-factory-boy==2.11.1
-faker==2.0.0
-filelock==3.0.12
-filemagic==1.6
-fuzzywuzzy[speedup]==0.15.0
-gunicorn==20.0.4
-idna==2.8
-imagesize==1.1.0
-importlib-metadata==0.19
-inotify-simple==1.1.8; sys_platform == 'linux'
-jinja2==2.10.1
-langdetect==1.0.7
-markupsafe==1.1.1
-more-itertools==7.2.0
-packaging==19.1
-pdftotext==2.1.1
-pillow==5.4.1
-pluggy==0.12.0
-ply==3.11
-psycopg2==2.8.4
-py==1.8.0
-pycodestyle==2.4.0
-pygments==2.4.2
-pyocr==0.5.3
-pyparsing==2.4.2
-pytest-cov==2.6.1
-pytest-django==3.4.5
-pytest-env==0.6.2
-pytest-forked==1.0.2
-pytest-sugar==0.9.2
-pytest-xdist==1.26.0
-pytest==4.1.1
-python-dateutil==2.7.5
-python-dotenv==0.10.1
-python-gnupg==0.4.4
-python-levenshtein==0.12.0
-pytz==2018.9
-regex==2019.6.8
-requests==2.22.0
-six==1.12.0
-snowballstemmer==1.9.0
-sphinx==1.8.3
-sphinxcontrib-applehelp==1.0.1
-sphinxcontrib-devhelp==1.0.1
-sphinxcontrib-htmlhelp==1.0.2
-sphinxcontrib-jsmath==1.0.1
-sphinxcontrib-qthelp==1.0.2
-sphinxcontrib-serializinghtml==1.1.3
-termcolor==1.1.0
-text-unidecode==1.2
-toml==0.10.0
-tox==3.7.0
-tzlocal==2.0.0
-urllib3==1.25.3
-virtualenv==16.7.2
-wcwidth==0.1.7
-whitenoise==4.1.3
-zipp==0.5.2
diff --git a/scripts/docker-entrypoint.sh b/scripts/docker-entrypoint.sh
deleted file mode 100644
index 2028413d1..000000000
--- a/scripts/docker-entrypoint.sh
+++ /dev/null
@@ -1,122 +0,0 @@
-#!/bin/bash
-set -e
-
-# Source: https://github.com/sameersbn/docker-gitlab/
-map_uidgid() {
- USERMAP_ORIG_UID=$(id -u paperless)
- USERMAP_ORIG_GID=$(id -g paperless)
- USERMAP_NEW_UID=${USERMAP_UID:-$USERMAP_ORIG_UID}
- USERMAP_NEW_GID=${USERMAP_GID:-${USERMAP_ORIG_GID:-$USERMAP_NEW_UID}}
- if [[ ${USERMAP_NEW_UID} != "${USERMAP_ORIG_UID}" || ${USERMAP_NEW_GID} != "${USERMAP_ORIG_GID}" ]]; then
- echo "Mapping UID and GID for paperless:paperless to $USERMAP_NEW_UID:$USERMAP_NEW_GID"
- usermod -u "${USERMAP_NEW_UID}" paperless
- groupmod -o -g "${USERMAP_NEW_GID}" paperless
- fi
-}
-
-set_permissions() {
- # Set permissions for consumption and export directory
- for dir in PAPERLESS_CONSUMPTION_DIR PAPERLESS_EXPORT_DIR; do
- # Extract the name of the current directory from $dir for the error message
- cur_dir_name=$(echo "$dir" | awk -F'_' '{ print tolower($2); }')
- chgrp paperless "${!dir}" || {
- echo "Changing group of ${cur_dir_name} directory:"
- echo " ${!dir}"
- echo "failed."
- echo ""
- echo "Either try to set it on your host-mounted directory"
- echo "directly, or make sure that the directory has \`g+wx\`"
- echo "permissions and the files in it at least \`o+r\`."
- } >&2
- chmod g+wx "${!dir}" || {
- echo "Changing group permissions of ${cur_dir_name} directory:"
- echo " ${!dir}"
- echo "failed."
- echo ""
- echo "Either try to set it on your host-mounted directory"
- echo "directly, or make sure that the directory has \`g+wx\`"
- echo "permissions and the files in it at least \`o+r\`."
- } >&2
- done
- # Set permissions for application directory
- chown -Rh paperless:paperless /usr/src/paperless
-}
-
-migrations() {
- # A simple lock file in case other containers use this startup
- LOCKFILE="/usr/src/paperless/data/db.sqlite3.migration"
-
- # check for and create lock file in one command
- if (set -o noclobber; echo "$$" > "${LOCKFILE}") 2> /dev/null
- then
- trap 'rm -f "${LOCKFILE}"; exit $?' INT TERM EXIT
- sudo -HEu paperless "/usr/src/paperless/src/manage.py" "migrate"
- rm ${LOCKFILE}
- fi
-}
-
-initialize() {
- map_uidgid
- set_permissions
- migrations
-}
-
-install_languages() {
- local langs="$1"
- read -ra langs <<<"$langs"
-
- # Check that it is not empty
- if [ ${#langs[@]} -eq 0 ]; then
- return
- fi
-
- # Loop over languages to be installed
- for lang in "${langs[@]}"; do
- pkg="tesseract-ocr-data-$lang"
-
- # English is installed by default
- if [[ "$lang" == "eng" ]]; then
- continue
- fi
-
- if apk info -e "$pkg" > /dev/null 2>&1; then
- continue
- fi
- if ! apk --no-cache info "$pkg" > /dev/null 2>&1; then
- continue
- fi
-
- apk --no-cache --update add "$pkg"
- done
-}
-
-
-if [[ "$1" != "/"* ]]; then
- initialize
-
- # Install additional languages if specified
- if [[ ! -z "$PAPERLESS_OCR_LANGUAGES" ]]; then
- install_languages "$PAPERLESS_OCR_LANGUAGES"
- fi
-
- if [[ "$1" = "gunicorn" ]]; then
- shift
- EXTRA_PARAMS=""
- SSL_KEY_PATH="/usr/src/paperless/data/ssl.key"
- SSL_CERT_PATH="/usr/src/paperless/data/ssl.cert"
- if [ "${PAPERLESS_USE_SSL}" = "true" ]; then
- if [ -f "${SSL_KEY_PATH}" ] && [ -f "${SSL_CERT_PATH}" ]; then
- EXTRA_PARAMS="--certfile=${SSL_CERT_PATH} --keyfile=${SSL_KEY_PATH}"
- else
- echo "Error: Could not find certfile in ${SSL_CERT_PATH} or keyfile in ${SSL_KEY_PATH}, but \$PAPERLESS_USE_SSL is true. Starting without SSL enabled."
- fi
- fi
- cd /usr/src/paperless/src/ && \
- exec sudo -HEu paperless /usr/bin/gunicorn -c /usr/src/paperless/gunicorn.conf ${EXTRA_PARAMS} "$@" paperless.wsgi
- else
- exec sudo -HEu paperless "/usr/src/paperless/src/manage.py" "$@"
- fi
-fi
-
-exec "$@"
-
diff --git a/scripts/make-release.sh b/scripts/make-release.sh
new file mode 100755
index 000000000..f5c9028fa
--- /dev/null
+++ b/scripts/make-release.sh
@@ -0,0 +1,125 @@
+#!/bin/bash
+
+# Release checklist
+# - wait for travis build.
+# adjust src/paperless/version.py
+# changelog in the documentation
+# adjust versions in docker/hub/*
+# adjust version in src-ui/src/environments/prod
+# If docker-compose was modified: all compose files are the same.
+
+# Steps:
+# run release script "dev", push
+# if it works: new tag, merge into master
+# on master: make release "lastest", push
+# on master: make release "version-tag", push
+# publish release files
+
+set -e
+
+
+VERSION=$1
+
+if [ -z "$VERSION" ]
+then
+ echo "Need a version string."
+ exit 1
+fi
+
+# source root directory of paperless
+PAPERLESS_ROOT=$(git rev-parse --show-toplevel)
+
+# output directory
+PAPERLESS_DIST="$PAPERLESS_ROOT/dist"
+PAPERLESS_DIST_APP="$PAPERLESS_DIST/paperless-ng"
+PAPERLESS_DIST_DOCKERFILES="$PAPERLESS_DIST/paperless-ng-dockerfiles"
+
+if [ -d "$PAPERLESS_DIST" ]
+then
+ echo "Removing $PAPERLESS_DIST"
+ rm "$PAPERLESS_DIST" -r
+fi
+
+mkdir "$PAPERLESS_DIST"
+mkdir "$PAPERLESS_DIST_APP"
+mkdir "$PAPERLESS_DIST_APP/docker"
+mkdir "$PAPERLESS_DIST_APP/scripts"
+mkdir "$PAPERLESS_DIST_DOCKERFILES"
+
+# setup dependencies.
+
+cd "$PAPERLESS_ROOT"
+
+pipenv clean
+pipenv install --dev
+pipenv lock --keep-outdated -r > "$PAPERLESS_DIST_APP/requirements.txt"
+
+# test if the application works.
+
+cd "$PAPERLESS_ROOT/src"
+pipenv run pytest --cov
+pipenv run pycodestyle
+
+# make the documentation.
+
+cd "$PAPERLESS_ROOT/docs"
+make clean html
+
+# copy stuff into place
+
+# the application itself
+
+cp "$PAPERLESS_ROOT/.env" \
+ "$PAPERLESS_ROOT/.dockerignore" \
+ "$PAPERLESS_ROOT/CONTRIBUTING.md" \
+ "$PAPERLESS_ROOT/LICENSE" \
+ "$PAPERLESS_ROOT/Pipfile" \
+ "$PAPERLESS_ROOT/Pipfile.lock" \
+ "$PAPERLESS_ROOT/README.md" "$PAPERLESS_DIST_APP"
+
+cp "$PAPERLESS_ROOT/paperless.conf.example" "$PAPERLESS_DIST_APP/paperless.conf"
+
+# copy python source, templates and static files.
+cd "$PAPERLESS_ROOT"
+find src -wholename '*/templates/*' -o -wholename '*/static/*' -o -name '*.py' | cpio -pdm "$PAPERLESS_DIST_APP"
+
+# build the front end.
+
+cd "$PAPERLESS_ROOT/src-ui"
+ng build --prod --output-hashing none --sourceMap=false --output-path "$PAPERLESS_DIST_APP/src/documents/static/frontend"
+
+# documentation
+cp "$PAPERLESS_ROOT/docs/_build/html/" "$PAPERLESS_DIST_APP/docs" -r
+
+# docker files for building the image yourself
+cp "$PAPERLESS_ROOT/docker/local/"* "$PAPERLESS_DIST_APP"
+cp "$PAPERLESS_ROOT/docker/docker-compose.env" "$PAPERLESS_DIST_APP"
+
+# docker files for pulling from docker hub
+cp "$PAPERLESS_ROOT/docker/hub/"* "$PAPERLESS_DIST_DOCKERFILES"
+cp "$PAPERLESS_ROOT/.env" "$PAPERLESS_DIST_DOCKERFILES"
+cp "$PAPERLESS_ROOT/docker/docker-compose.env" "$PAPERLESS_DIST_DOCKERFILES"
+
+# auxiliary files required for the docker image
+cp "$PAPERLESS_ROOT/docker/docker-entrypoint.sh" "$PAPERLESS_DIST_APP/docker/"
+cp "$PAPERLESS_ROOT/docker/gunicorn.conf.py" "$PAPERLESS_DIST_APP/docker/"
+cp "$PAPERLESS_ROOT/docker/imagemagick-policy.xml" "$PAPERLESS_DIST_APP/docker/"
+cp "$PAPERLESS_ROOT/docker/supervisord.conf" "$PAPERLESS_DIST_APP/docker/"
+
+# auxiliary files for bare metal installs
+cp "$PAPERLESS_ROOT/scripts/paperless-webserver.service" "$PAPERLESS_DIST_APP/scripts/"
+cp "$PAPERLESS_ROOT/scripts/paperless-consumer.service" "$PAPERLESS_DIST_APP/scripts/"
+cp "$PAPERLESS_ROOT/scripts/paperless-scheduler.service" "$PAPERLESS_DIST_APP/scripts/"
+
+# try to make the docker build.
+
+cd "$PAPERLESS_DIST_APP"
+
+docker build . -t "jonaswinkler/paperless-ng:$VERSION"
+
+# works. package the app!
+
+cd "$PAPERLESS_DIST"
+
+tar -cJf "paperless-ng-$VERSION.tar.xz" paperless-ng/
+tar -cJf "paperless-ng-$VERSION-dockerfiles.tar.xz" paperless-ng-dockerfiles/
diff --git a/scripts/paperless-consumer.service b/scripts/paperless-consumer.service
index 79a27d3ce..04512b719 100644
--- a/scripts/paperless-consumer.service
+++ b/scripts/paperless-consumer.service
@@ -1,10 +1,12 @@
[Unit]
Description=Paperless consumer
+Requires=redis.service
[Service]
User=paperless
Group=paperless
-ExecStart=/home/paperless/project/virtualenv/bin/python /home/paperless/project/src/manage.py document_consumer
+WorkingDirectory=/opt/paperless/src
+ExecStart=python3 manage.py document_consumer
[Install]
WantedBy=multi-user.target
diff --git a/scripts/paperless-scheduler.service b/scripts/paperless-scheduler.service
new file mode 100644
index 000000000..c565d8af4
--- /dev/null
+++ b/scripts/paperless-scheduler.service
@@ -0,0 +1,12 @@
+[Unit]
+Description=Paperless consumer
+Requires=redis.service
+
+[Service]
+User=paperless
+Group=paperless
+WorkingDirectory=/opt/paperless/src
+ExecStart=python3 manage.py qcluster
+
+[Install]
+WantedBy=multi-user.target
diff --git a/scripts/paperless-webserver.service b/scripts/paperless-webserver.service
index 8ff746be2..a43110be2 100644
--- a/scripts/paperless-webserver.service
+++ b/scripts/paperless-webserver.service
@@ -2,11 +2,13 @@
Description=Paperless webserver
After=network.target
Wants=network.target
+Requires=redis.service
[Service]
User=paperless
Group=paperless
-ExecStart=/home/paperless/project/virtualenv/bin/gunicorn --pythonpath=/home/paperless/project/src paperless.wsgi -w 2
+WorkingDirectory=/opt/paperless/src
+ExecStart=/opt/paperless/.local/bin/gunicorn paperless.wsgi -w 2 -b 0.0.0.0:8000
[Install]
WantedBy=multi-user.target
diff --git a/scripts/push-release.sh b/scripts/push-release.sh
new file mode 100755
index 000000000..cfa63f5cf
--- /dev/null
+++ b/scripts/push-release.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+set -e
+
+
+VERSION=$1
+
+if [ -z "$VERSION" ]
+then
+ echo "Need a version string."
+ exit 1
+fi
+
+# source root directory of paperless
+PAPERLESS_ROOT=$(git rev-parse --show-toplevel)
+
+# output directory
+PAPERLESS_DIST="$PAPERLESS_ROOT/dist"
+PAPERLESS_DIST_APP="$PAPERLESS_DIST/paperless-ng"
+
+cd "$PAPERLESS_DIST_APP"
+
+docker push "jonaswinkler/paperless-ng:$VERSION"
diff --git a/scripts/start_services.sh b/scripts/start_services.sh
new file mode 100755
index 000000000..e566f59b3
--- /dev/null
+++ b/scripts/start_services.sh
@@ -0,0 +1,2 @@
+docker run -p 5432:5432 -v paperless_pgdata:/var/lib/postgresql/data -d postgres:13
+docker run -d -p 6379:6379 redis:latest
diff --git a/src-ui/.browserslistrc b/src-ui/.browserslistrc
new file mode 100644
index 000000000..0ccadaf32
--- /dev/null
+++ b/src-ui/.browserslistrc
@@ -0,0 +1,18 @@
+# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
+# For additional information regarding the format and rule options, please see:
+# https://github.com/browserslist/browserslist#queries
+
+# For the full list of supported browsers by the Angular framework, please see:
+# https://angular.io/guide/browser-support
+
+# You can see what browsers were selected by your queries by running:
+# npx browserslist
+
+last 1 Chrome version
+last 1 Firefox version
+last 2 Edge major versions
+last 2 Safari major versions
+last 2 iOS major versions
+Firefox ESR
+not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the 'not' prefix on this line.
+not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
diff --git a/src-ui/.editorconfig b/src-ui/.editorconfig
new file mode 100644
index 000000000..59d9a3a3e
--- /dev/null
+++ b/src-ui/.editorconfig
@@ -0,0 +1,16 @@
+# Editor configuration, see https://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.ts]
+quote_type = single
+
+[*.md]
+max_line_length = off
+trim_trailing_whitespace = false
diff --git a/src-ui/.gitignore b/src-ui/.gitignore
new file mode 100644
index 000000000..86d943a9b
--- /dev/null
+++ b/src-ui/.gitignore
@@ -0,0 +1,46 @@
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+
+# compiled output
+/dist
+/tmp
+/out-tsc
+# Only exists if Bazel was run
+/bazel-out
+
+# dependencies
+/node_modules
+
+# profiling files
+chrome-profiler-events*.json
+speed-measure-plugin*.json
+
+# IDEs and editors
+/.idea
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+
+# IDE - VSCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+.history/*
+
+# misc
+/.sass-cache
+/connect.lock
+/coverage
+/libpeerconnection.log
+npm-debug.log
+yarn-error.log
+testem.log
+/typings
+
+# System Files
+.DS_Store
+Thumbs.db
diff --git a/src-ui/README.md b/src-ui/README.md
new file mode 100644
index 000000000..568ba0874
--- /dev/null
+++ b/src-ui/README.md
@@ -0,0 +1,27 @@
+# PaperlessUi
+
+This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.1.5.
+
+## Development server
+
+Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
+
+## Code scaffolding
+
+Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
+
+## Build
+
+Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
+
+## Running unit tests
+
+Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
+
+## Running end-to-end tests
+
+Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
+
+## Further help
+
+To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
diff --git a/src-ui/angular.json b/src-ui/angular.json
new file mode 100644
index 000000000..2ff1bb3b0
--- /dev/null
+++ b/src-ui/angular.json
@@ -0,0 +1,130 @@
+{
+ "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+ "version": 1,
+ "newProjectRoot": "projects",
+ "projects": {
+ "paperless-ui": {
+ "projectType": "application",
+ "schematics": {
+ "@schematics/angular:component": {
+ "style": "scss"
+ }
+ },
+ "root": "",
+ "sourceRoot": "src",
+ "prefix": "app",
+ "architect": {
+ "build": {
+ "builder": "@angular-devkit/build-angular:browser",
+ "options": {
+ "outputPath": "dist/paperless-ui",
+ "outputHashing": "none",
+ "index": "src/index.html",
+ "main": "src/main.ts",
+ "polyfills": "src/polyfills.ts",
+ "tsConfig": "tsconfig.app.json",
+ "aot": true,
+ "assets": [
+ "src/favicon.ico",
+ "src/assets"
+ ],
+ "styles": [
+ "src/styles.scss"
+ ],
+ "scripts": []
+ },
+ "configurations": {
+ "production": {
+ "fileReplacements": [
+ {
+ "replace": "src/environments/environment.ts",
+ "with": "src/environments/environment.prod.ts"
+ }
+ ],
+ "optimization": true,
+ "outputHashing": "none",
+ "sourceMap": false,
+ "extractCss": true,
+ "namedChunks": false,
+ "extractLicenses": true,
+ "vendorChunk": false,
+ "buildOptimizer": true,
+ "budgets": [
+ {
+ "type": "initial",
+ "maximumWarning": "2mb",
+ "maximumError": "5mb"
+ },
+ {
+ "type": "anyComponentStyle",
+ "maximumWarning": "6kb",
+ "maximumError": "10kb"
+ }
+ ]
+ }
+ }
+ },
+ "serve": {
+ "builder": "@angular-devkit/build-angular:dev-server",
+ "options": {
+ "browserTarget": "paperless-ui:build"
+ },
+ "configurations": {
+ "production": {
+ "browserTarget": "paperless-ui:build:production"
+ }
+ }
+ },
+ "extract-i18n": {
+ "builder": "@angular-devkit/build-angular:extract-i18n",
+ "options": {
+ "browserTarget": "paperless-ui:build"
+ }
+ },
+ "test": {
+ "builder": "@angular-devkit/build-angular:karma",
+ "options": {
+ "main": "src/test.ts",
+ "polyfills": "src/polyfills.ts",
+ "tsConfig": "tsconfig.spec.json",
+ "karmaConfig": "karma.conf.js",
+ "assets": [
+ "src/favicon.ico",
+ "src/assets"
+ ],
+ "styles": [
+ "src/styles.scss"
+ ],
+ "scripts": []
+ }
+ },
+ "lint": {
+ "builder": "@angular-devkit/build-angular:tslint",
+ "options": {
+ "tsConfig": [
+ "tsconfig.app.json",
+ "tsconfig.spec.json",
+ "e2e/tsconfig.json"
+ ],
+ "exclude": [
+ "**/node_modules/**"
+ ]
+ }
+ },
+ "e2e": {
+ "builder": "@angular-devkit/build-angular:protractor",
+ "options": {
+ "protractorConfig": "e2e/protractor.conf.js",
+ "devServerTarget": "paperless-ui:serve"
+ },
+ "configurations": {
+ "production": {
+ "devServerTarget": "paperless-ui:serve:production"
+ }
+ }
+ }
+ }
+ }
+ },
+ "defaultProject": "paperless-ui"
+}
\ No newline at end of file
diff --git a/src-ui/e2e/protractor.conf.js b/src-ui/e2e/protractor.conf.js
new file mode 100644
index 000000000..f238c0bbe
--- /dev/null
+++ b/src-ui/e2e/protractor.conf.js
@@ -0,0 +1,36 @@
+// @ts-check
+// Protractor configuration file, see link for more information
+// https://github.com/angular/protractor/blob/master/lib/config.ts
+
+const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
+
+/**
+ * @type { import("protractor").Config }
+ */
+exports.config = {
+ allScriptsTimeout: 11000,
+ specs: [
+ './src/**/*.e2e-spec.ts'
+ ],
+ capabilities: {
+ browserName: 'chrome'
+ },
+ directConnect: true,
+ baseUrl: 'http://localhost:4200/',
+ framework: 'jasmine',
+ jasmineNodeOpts: {
+ showColors: true,
+ defaultTimeoutInterval: 30000,
+ print: function() {}
+ },
+ onPrepare() {
+ require('ts-node').register({
+ project: require('path').join(__dirname, './tsconfig.json')
+ });
+ jasmine.getEnv().addReporter(new SpecReporter({
+ spec: {
+ displayStacktrace: StacktraceOption.PRETTY
+ }
+ }));
+ }
+};
\ No newline at end of file
diff --git a/src-ui/e2e/src/app.e2e-spec.ts b/src-ui/e2e/src/app.e2e-spec.ts
new file mode 100644
index 000000000..f2348e0c4
--- /dev/null
+++ b/src-ui/e2e/src/app.e2e-spec.ts
@@ -0,0 +1,23 @@
+import { AppPage } from './app.po';
+import { browser, logging } from 'protractor';
+
+describe('workspace-project App', () => {
+ let page: AppPage;
+
+ beforeEach(() => {
+ page = new AppPage();
+ });
+
+ it('should display welcome message', () => {
+ page.navigateTo();
+ expect(page.getTitleText()).toEqual('paperless-ui app is running!');
+ });
+
+ afterEach(async () => {
+ // Assert that there are no errors emitted from the browser
+ const logs = await browser.manage().logs().get(logging.Type.BROWSER);
+ expect(logs).not.toContain(jasmine.objectContaining({
+ level: logging.Level.SEVERE,
+ } as logging.Entry));
+ });
+});
diff --git a/src-ui/e2e/src/app.po.ts b/src-ui/e2e/src/app.po.ts
new file mode 100644
index 000000000..b68475e0f
--- /dev/null
+++ b/src-ui/e2e/src/app.po.ts
@@ -0,0 +1,11 @@
+import { browser, by, element } from 'protractor';
+
+export class AppPage {
+ navigateTo(): Promise {
+ return browser.get(browser.baseUrl) as Promise;
+ }
+
+ getTitleText(): Promise {
+ return element(by.css('app-root .content span')).getText() as Promise;
+ }
+}
diff --git a/src-ui/e2e/tsconfig.json b/src-ui/e2e/tsconfig.json
new file mode 100644
index 000000000..426058ef0
--- /dev/null
+++ b/src-ui/e2e/tsconfig.json
@@ -0,0 +1,14 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../out-tsc/e2e",
+ "module": "commonjs",
+ "target": "es2018",
+ "types": [
+ "jasmine",
+ "jasminewd2",
+ "node"
+ ]
+ }
+}
diff --git a/src-ui/karma.conf.js b/src-ui/karma.conf.js
new file mode 100644
index 000000000..9fb9e914b
--- /dev/null
+++ b/src-ui/karma.conf.js
@@ -0,0 +1,32 @@
+// Karma configuration file, see link for more information
+// https://karma-runner.github.io/1.0/config/configuration-file.html
+
+module.exports = function (config) {
+ config.set({
+ basePath: '',
+ frameworks: ['jasmine', '@angular-devkit/build-angular'],
+ plugins: [
+ require('karma-jasmine'),
+ require('karma-chrome-launcher'),
+ require('karma-jasmine-html-reporter'),
+ require('karma-coverage-istanbul-reporter'),
+ require('@angular-devkit/build-angular/plugins/karma')
+ ],
+ client: {
+ clearContext: false // leave Jasmine Spec Runner output visible in browser
+ },
+ coverageIstanbulReporter: {
+ dir: require('path').join(__dirname, './coverage/paperless-ui'),
+ reports: ['html', 'lcovonly', 'text-summary'],
+ fixWebpackSourcePaths: true
+ },
+ reporters: ['progress', 'kjhtml'],
+ port: 9876,
+ colors: true,
+ logLevel: config.LOG_INFO,
+ autoWatch: true,
+ browsers: ['Chrome'],
+ singleRun: false,
+ restartOnFileChange: true
+ });
+};
diff --git a/src-ui/package-lock.json b/src-ui/package-lock.json
new file mode 100644
index 000000000..5eca0b3c0
--- /dev/null
+++ b/src-ui/package-lock.json
@@ -0,0 +1,14303 @@
+{
+ "name": "paperless-ui",
+ "version": "0.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@angular-devkit/architect": {
+ "version": "0.1001.5",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1001.5.tgz",
+ "integrity": "sha512-W8ZqtbxwDtHnzPoqVyeyDEq24i+H0/i0fjIBuJ+XAMtd3U9JtPALIRLdhnunLXO7OLxjtxjzh0qLxKgiXGEd3g==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/core": "10.1.5",
+ "rxjs": "6.6.2"
+ },
+ "dependencies": {
+ "rxjs": {
+ "version": "6.6.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz",
+ "integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "tslib": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.0.tgz",
+ "integrity": "sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw==",
+ "dev": true
+ }
+ }
+ },
+ "@angular-devkit/build-angular": {
+ "version": "0.1002.0",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-0.1002.0.tgz",
+ "integrity": "sha512-cPkdp1GceokGHc79Wg0hACMqqmnJ4W3H9kY4c9qp1Xz18b3vk1aq09JNawOpfUN09S9vBCnn4glg22lRyqmJNA==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/architect": "0.1002.0",
+ "@angular-devkit/build-optimizer": "0.1002.0",
+ "@angular-devkit/build-webpack": "0.1002.0",
+ "@angular-devkit/core": "10.2.0",
+ "@babel/core": "7.11.1",
+ "@babel/generator": "7.11.0",
+ "@babel/plugin-transform-runtime": "7.11.0",
+ "@babel/preset-env": "7.11.0",
+ "@babel/runtime": "7.11.2",
+ "@babel/template": "7.10.4",
+ "@jsdevtools/coverage-istanbul-loader": "3.0.5",
+ "@ngtools/webpack": "10.2.0",
+ "autoprefixer": "9.8.6",
+ "babel-loader": "8.1.0",
+ "browserslist": "^4.9.1",
+ "cacache": "15.0.5",
+ "caniuse-lite": "^1.0.30001032",
+ "circular-dependency-plugin": "5.2.0",
+ "copy-webpack-plugin": "6.0.3",
+ "core-js": "3.6.4",
+ "css-loader": "4.2.2",
+ "cssnano": "4.1.10",
+ "file-loader": "6.0.0",
+ "find-cache-dir": "3.3.1",
+ "glob": "7.1.6",
+ "jest-worker": "26.3.0",
+ "karma-source-map-support": "1.4.0",
+ "less-loader": "6.2.0",
+ "license-webpack-plugin": "2.3.0",
+ "loader-utils": "2.0.0",
+ "mini-css-extract-plugin": "0.10.0",
+ "minimatch": "3.0.4",
+ "open": "7.2.0",
+ "parse5": "6.0.1",
+ "parse5-htmlparser2-tree-adapter": "6.0.1",
+ "pnp-webpack-plugin": "1.6.4",
+ "postcss": "7.0.32",
+ "postcss-import": "12.0.1",
+ "postcss-loader": "3.0.0",
+ "raw-loader": "4.0.1",
+ "regenerator-runtime": "0.13.7",
+ "resolve-url-loader": "3.1.2",
+ "rimraf": "3.0.2",
+ "rollup": "2.26.5",
+ "rxjs": "6.6.2",
+ "sass": "1.26.10",
+ "sass-loader": "10.0.1",
+ "semver": "7.3.2",
+ "source-map": "0.7.3",
+ "source-map-loader": "1.0.2",
+ "source-map-support": "0.5.19",
+ "speed-measure-webpack-plugin": "1.3.3",
+ "style-loader": "1.2.1",
+ "stylus": "0.54.8",
+ "stylus-loader": "3.0.2",
+ "terser": "5.3.0",
+ "terser-webpack-plugin": "4.1.0",
+ "tree-kill": "1.2.2",
+ "webpack": "4.44.1",
+ "webpack-dev-middleware": "3.7.2",
+ "webpack-dev-server": "3.11.0",
+ "webpack-merge": "4.2.2",
+ "webpack-sources": "1.4.3",
+ "webpack-subresource-integrity": "1.4.1",
+ "worker-plugin": "5.0.0"
+ },
+ "dependencies": {
+ "@angular-devkit/architect": {
+ "version": "0.1002.0",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1002.0.tgz",
+ "integrity": "sha512-twM8V03ujBIGVpgV1PBlSDodUdxtUb7WakutfWafAvEHUsgwzfvQz2VtKWvjNZ9AiYjnCuwkQaclqVv0VHNo9w==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/core": "10.2.0",
+ "rxjs": "6.6.2"
+ }
+ },
+ "@angular-devkit/core": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-10.2.0.tgz",
+ "integrity": "sha512-XAszFhSF3mZw1VjoOsYGbArr5NJLcStjOvcCGjBPl1UBM2AKpuCQXHxI9XJGYKL3B93Vp5G58d8qkHvamT53OA==",
+ "dev": true,
+ "requires": {
+ "ajv": "6.12.4",
+ "fast-json-stable-stringify": "2.1.0",
+ "magic-string": "0.25.7",
+ "rxjs": "6.6.2",
+ "source-map": "0.7.3"
+ }
+ },
+ "rxjs": {
+ "version": "6.6.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz",
+ "integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
+ }
+ },
+ "@angular-devkit/build-optimizer": {
+ "version": "0.1002.0",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/build-optimizer/-/build-optimizer-0.1002.0.tgz",
+ "integrity": "sha512-ACnm9doPMbRtSy1UZN5ir7smeLMx0g0oW7jX3jyPepeQKZ+9U1Bn09t10NLZQH+Z509jWZgvNJH/aOh85P6euw==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "2.0.0",
+ "source-map": "0.7.3",
+ "tslib": "2.0.1",
+ "typescript": "4.0.2",
+ "webpack-sources": "1.4.3"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz",
+ "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==",
+ "dev": true
+ },
+ "typescript": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz",
+ "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==",
+ "dev": true
+ }
+ }
+ },
+ "@angular-devkit/build-webpack": {
+ "version": "0.1002.0",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1002.0.tgz",
+ "integrity": "sha512-TLBBQ6ANOLKXOPxpCOnxAtoknwHA7XhsLuueN06w5qqF+QNNbWUMPoieKFGs2TnotfCgbiq6x57IDEZTyT6V0w==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/architect": "0.1002.0",
+ "@angular-devkit/core": "10.2.0",
+ "rxjs": "6.6.2"
+ },
+ "dependencies": {
+ "@angular-devkit/architect": {
+ "version": "0.1002.0",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1002.0.tgz",
+ "integrity": "sha512-twM8V03ujBIGVpgV1PBlSDodUdxtUb7WakutfWafAvEHUsgwzfvQz2VtKWvjNZ9AiYjnCuwkQaclqVv0VHNo9w==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/core": "10.2.0",
+ "rxjs": "6.6.2"
+ }
+ },
+ "@angular-devkit/core": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-10.2.0.tgz",
+ "integrity": "sha512-XAszFhSF3mZw1VjoOsYGbArr5NJLcStjOvcCGjBPl1UBM2AKpuCQXHxI9XJGYKL3B93Vp5G58d8qkHvamT53OA==",
+ "dev": true,
+ "requires": {
+ "ajv": "6.12.4",
+ "fast-json-stable-stringify": "2.1.0",
+ "magic-string": "0.25.7",
+ "rxjs": "6.6.2",
+ "source-map": "0.7.3"
+ }
+ },
+ "rxjs": {
+ "version": "6.6.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz",
+ "integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
+ }
+ },
+ "@angular-devkit/core": {
+ "version": "10.1.5",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-10.1.5.tgz",
+ "integrity": "sha512-Ly97h90Z6ZLhSnTkk2baUDNLeOrKgj/bUPkcBEKWranx6IRx8FMzin/+ysIQasBlEXWPIc8QbBmCz7xXkO4p7g==",
+ "dev": true,
+ "requires": {
+ "ajv": "6.12.4",
+ "fast-json-stable-stringify": "2.1.0",
+ "magic-string": "0.25.7",
+ "rxjs": "6.6.2",
+ "source-map": "0.7.3"
+ },
+ "dependencies": {
+ "rxjs": {
+ "version": "6.6.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz",
+ "integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "tslib": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.0.tgz",
+ "integrity": "sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw==",
+ "dev": true
+ }
+ }
+ },
+ "@angular-devkit/schematics": {
+ "version": "10.1.5",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-10.1.5.tgz",
+ "integrity": "sha512-5bhQX/PC548wIPcgCx9Q0Oewe8/i8+0eZvD9qLVWzJvUEKqgbjgoA7r7KJIJx2WINbESJGTIjwbXSZ6JmAJNhA==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/core": "10.1.5",
+ "ora": "5.0.0",
+ "rxjs": "6.6.2"
+ },
+ "dependencies": {
+ "rxjs": {
+ "version": "6.6.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz",
+ "integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "tslib": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.0.tgz",
+ "integrity": "sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw==",
+ "dev": true
+ }
+ }
+ },
+ "@angular/animations": {
+ "version": "10.1.5",
+ "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-10.1.5.tgz",
+ "integrity": "sha512-RbUIluxgE5pSWWdODlcEAQuRqc/D1A2v275zBsMFjwJg3/cZl/z+RWcFJedHpJHEtbz7Aay1UWHu9jhXfA8elg==",
+ "requires": {
+ "tslib": "^2.0.0"
+ }
+ },
+ "@angular/cli": {
+ "version": "10.1.5",
+ "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-10.1.5.tgz",
+ "integrity": "sha512-HlJVDxuTfrmxp8CvABV1pn7Ffeo0q0PuAR7gNCDcVi2vN7EDmBRRnyxBvATO4KzE5DHiSIqF0xLIsokSS7JC6w==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/architect": "0.1001.5",
+ "@angular-devkit/core": "10.1.5",
+ "@angular-devkit/schematics": "10.1.5",
+ "@schematics/angular": "10.1.5",
+ "@schematics/update": "0.1001.5",
+ "@yarnpkg/lockfile": "1.1.0",
+ "ansi-colors": "4.1.1",
+ "debug": "4.1.1",
+ "ini": "1.3.5",
+ "inquirer": "7.3.3",
+ "npm-package-arg": "8.0.1",
+ "npm-pick-manifest": "6.1.0",
+ "open": "7.2.0",
+ "pacote": "9.5.12",
+ "read-package-tree": "5.3.1",
+ "rimraf": "3.0.2",
+ "semver": "7.3.2",
+ "symbol-observable": "1.2.0",
+ "universal-analytics": "0.4.23",
+ "uuid": "8.3.0"
+ },
+ "dependencies": {
+ "ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "uuid": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz",
+ "integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==",
+ "dev": true
+ }
+ }
+ },
+ "@angular/common": {
+ "version": "10.1.5",
+ "resolved": "https://registry.npmjs.org/@angular/common/-/common-10.1.5.tgz",
+ "integrity": "sha512-xo10mSQYuf6x1XrnTfwt3Rs7JtSMkSyrJtAS/vNQKdBP/8zmn6pP9zRpp7vhQ5qF+W3HN8rPLb+YI2F6uaGjBg==",
+ "requires": {
+ "tslib": "^2.0.0"
+ }
+ },
+ "@angular/compiler": {
+ "version": "10.1.5",
+ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-10.1.5.tgz",
+ "integrity": "sha512-3LyFkEzs6P6YYKkE/6E4PasMd58EBddOt9kR9kPmj9Atv/BLY3nc5RSWkOe4rK4GnBVP+ByzQiT9Fn5CiQnG/g==",
+ "requires": {
+ "tslib": "^2.0.0"
+ }
+ },
+ "@angular/compiler-cli": {
+ "version": "10.1.5",
+ "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-10.1.5.tgz",
+ "integrity": "sha512-AJ4eOHUxgDdfq/EagUlhJ6HaNlHajtmPkhXp2HmNMNN1nPN55VZSvN43Co2gdAHiFENqsTNlnQH630aXaDyVbQ==",
+ "dev": true,
+ "requires": {
+ "canonical-path": "1.0.0",
+ "chokidar": "^3.0.0",
+ "convert-source-map": "^1.5.1",
+ "dependency-graph": "^0.7.2",
+ "fs-extra": "4.0.2",
+ "magic-string": "^0.25.0",
+ "minimist": "^1.2.0",
+ "reflect-metadata": "^0.1.2",
+ "semver": "^6.3.0",
+ "source-map": "^0.6.1",
+ "sourcemap-codec": "^1.4.8",
+ "tslib": "^2.0.0",
+ "yargs": "15.3.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
+ },
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+ "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "yargs": {
+ "version": "15.3.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.0.tgz",
+ "integrity": "sha512-g/QCnmjgOl1YJjGsnUg2SatC7NUYEiLXJqxNOQU9qSpjzGtGXda9b+OKccr1kLTy8BN9yqEyqfq5lxlwdc13TA==",
+ "dev": true,
+ "requires": {
+ "cliui": "^6.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^4.1.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^4.2.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^18.1.0"
+ }
+ },
+ "yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
+ "@angular/core": {
+ "version": "10.1.5",
+ "resolved": "https://registry.npmjs.org/@angular/core/-/core-10.1.5.tgz",
+ "integrity": "sha512-B8j1B5vkBmzyan78kMJhw7dfhe7znmujbeDU7qRgRcIllc9pVJv7D133Yze6JFiLVg21PfyFYs8FBJNeq39hxQ==",
+ "requires": {
+ "tslib": "^2.0.0"
+ }
+ },
+ "@angular/forms": {
+ "version": "10.1.5",
+ "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-10.1.5.tgz",
+ "integrity": "sha512-fkXKCwXL0XeFMUkmzJpm+FHYrv1CCfFGxYEBQ/bzfd3Op+dFJqEPiOwK3wG943Y09THday6H509RwwEIyF/4yw==",
+ "requires": {
+ "tslib": "^2.0.0"
+ }
+ },
+ "@angular/localize": {
+ "version": "10.1.5",
+ "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-10.1.5.tgz",
+ "integrity": "sha512-wEiF9lLWulTwGFqFJ4dJ+mBiyylE0amJR4leUGHKu6iwjPUQUBCqReCapfvLLTfgt3nbq7DtVqlqxj7KW7w+xg==",
+ "requires": {
+ "@babel/core": "7.8.3",
+ "glob": "7.1.2",
+ "yargs": "15.3.0"
+ },
+ "dependencies": {
+ "@babel/core": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.3.tgz",
+ "integrity": "sha512-4XFkf8AwyrEG7Ziu3L2L0Cv+WyY47Tcsp70JFmpftbAA1K7YL/sgE9jh9HyNj08Y/U50ItUchpN0w6HxAoX1rA==",
+ "requires": {
+ "@babel/code-frame": "^7.8.3",
+ "@babel/generator": "^7.8.3",
+ "@babel/helpers": "^7.8.3",
+ "@babel/parser": "^7.8.3",
+ "@babel/template": "^7.8.3",
+ "@babel/traverse": "^7.8.3",
+ "@babel/types": "^7.8.3",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.1",
+ "json5": "^2.1.0",
+ "lodash": "^4.17.13",
+ "resolve": "^1.3.2",
+ "semver": "^5.4.1",
+ "source-map": "^0.5.0"
+ }
+ },
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg=="
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
+ },
+ "cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+ },
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "requires": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
+ },
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
+ },
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+ },
+ "string-width": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+ "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "yargs": {
+ "version": "15.3.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.0.tgz",
+ "integrity": "sha512-g/QCnmjgOl1YJjGsnUg2SatC7NUYEiLXJqxNOQU9qSpjzGtGXda9b+OKccr1kLTy8BN9yqEyqfq5lxlwdc13TA==",
+ "requires": {
+ "cliui": "^6.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^4.1.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^4.2.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^18.1.0"
+ }
+ },
+ "yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
+ "@angular/platform-browser": {
+ "version": "10.1.5",
+ "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-10.1.5.tgz",
+ "integrity": "sha512-qMAoPHt6dgXMtieI4zx/s5yX7FFRRUDp1R4GMBCZHPN3p66WdEVxBJo4p5RWhZJioXpUwKz8Xvc+Rrh7r0KDBA==",
+ "requires": {
+ "tslib": "^2.0.0"
+ }
+ },
+ "@angular/platform-browser-dynamic": {
+ "version": "10.1.5",
+ "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-10.1.5.tgz",
+ "integrity": "sha512-wxHm1UFCtB+oU+IJ6pACGmjO9H8KVzJOLYL5hp2w0k8s7k7Zg73f6BdRgWWEEYv6uYIfF77qtKwgbH0X5H9S+w==",
+ "requires": {
+ "tslib": "^2.0.0"
+ }
+ },
+ "@angular/router": {
+ "version": "10.1.5",
+ "resolved": "https://registry.npmjs.org/@angular/router/-/router-10.1.5.tgz",
+ "integrity": "sha512-tY88ZzoBrc9K67wi5V1NLnurd3r9bYR2csZ6/zJeOE+Vdxz9ChSaglgh9T0vQdbVEAjVGPP5QtYaFO2Xv4qOIg==",
+ "requires": {
+ "tslib": "^2.0.0"
+ }
+ },
+ "@babel/code-frame": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+ "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+ "requires": {
+ "@babel/highlight": "^7.10.4"
+ }
+ },
+ "@babel/compat-data": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.12.1.tgz",
+ "integrity": "sha512-725AQupWJZ8ba0jbKceeFblZTY90McUBWMwHhkFQ9q1zKPJ95GUktljFcgcsIVwRnTnRKlcYzfiNImg5G9m6ZQ==",
+ "dev": true
+ },
+ "@babel/core": {
+ "version": "7.11.1",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.1.tgz",
+ "integrity": "sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/generator": "^7.11.0",
+ "@babel/helper-module-transforms": "^7.11.0",
+ "@babel/helpers": "^7.10.4",
+ "@babel/parser": "^7.11.1",
+ "@babel/template": "^7.10.4",
+ "@babel/traverse": "^7.11.0",
+ "@babel/types": "^7.11.0",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.1",
+ "json5": "^2.1.2",
+ "lodash": "^4.17.19",
+ "resolve": "^1.3.2",
+ "semver": "^5.4.1",
+ "source-map": "^0.5.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
+ "@babel/generator": {
+ "version": "7.11.0",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz",
+ "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==",
+ "requires": {
+ "@babel/types": "^7.11.0",
+ "jsesc": "^2.5.1",
+ "source-map": "^0.5.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+ }
+ }
+ },
+ "@babel/helper-annotate-as-pure": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz",
+ "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-builder-binary-assignment-operator-visitor": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz",
+ "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-explode-assignable-expression": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-compilation-targets": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.1.tgz",
+ "integrity": "sha512-jtBEif7jsPwP27GPHs06v4WBV0KrE8a/P7n0N0sSvHn2hwUCYnolP/CLmz51IzAW4NlN+HuoBtb9QcwnRo9F/g==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.12.1",
+ "@babel/helper-validator-option": "^7.12.1",
+ "browserslist": "^4.12.0",
+ "semver": "^5.5.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/helper-create-class-features-plugin": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.12.1.tgz",
+ "integrity": "sha512-hkL++rWeta/OVOBTRJc9a5Azh5mt5WgZUGAKMD8JM141YsE08K//bp1unBBieO6rUKkIPyUE0USQ30jAy3Sk1w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.10.4",
+ "@babel/helper-member-expression-to-functions": "^7.12.1",
+ "@babel/helper-optimise-call-expression": "^7.10.4",
+ "@babel/helper-replace-supers": "^7.12.1",
+ "@babel/helper-split-export-declaration": "^7.10.4"
+ }
+ },
+ "@babel/helper-create-regexp-features-plugin": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.1.tgz",
+ "integrity": "sha512-rsZ4LGvFTZnzdNZR5HZdmJVuXK8834R5QkF3WvcnBhrlVtF0HSIUC6zbreL9MgjTywhKokn8RIYRiq99+DLAxA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.10.4",
+ "@babel/helper-regex": "^7.10.4",
+ "regexpu-core": "^4.7.1"
+ }
+ },
+ "@babel/helper-define-map": {
+ "version": "7.10.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz",
+ "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.10.4",
+ "@babel/types": "^7.10.5",
+ "lodash": "^4.17.19"
+ }
+ },
+ "@babel/helper-explode-assignable-expression": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.12.1.tgz",
+ "integrity": "sha512-dmUwH8XmlrUpVqgtZ737tK88v07l840z9j3OEhCLwKTkjlvKpfqXVIZ0wpK3aeOxspwGrf/5AP5qLx4rO3w5rA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.12.1"
+ },
+ "dependencies": {
+ "@babel/types": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz",
+ "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ }
+ }
+ },
+ "@babel/helper-function-name": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz",
+ "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==",
+ "requires": {
+ "@babel/helper-get-function-arity": "^7.10.4",
+ "@babel/template": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-get-function-arity": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz",
+ "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==",
+ "requires": {
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-hoist-variables": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz",
+ "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-member-expression-to-functions": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.12.1.tgz",
+ "integrity": "sha512-k0CIe3tXUKTRSoEx1LQEPFU9vRQfqHtl+kf8eNnDqb4AUJEy5pz6aIiog+YWtVm2jpggjS1laH68bPsR+KWWPQ==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.12.1"
+ },
+ "dependencies": {
+ "@babel/types": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz",
+ "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ }
+ }
+ },
+ "@babel/helper-module-imports": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.12.1.tgz",
+ "integrity": "sha512-ZeC1TlMSvikvJNy1v/wPIazCu3NdOwgYZLIkmIyAsGhqkNpiDoQQRmaCK8YP4Pq3GPTLPV9WXaPCJKvx06JxKA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.12.1"
+ },
+ "dependencies": {
+ "@babel/types": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz",
+ "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ }
+ }
+ },
+ "@babel/helper-module-transforms": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.12.1.tgz",
+ "integrity": "sha512-QQzehgFAZ2bbISiCpmVGfiGux8YVFXQ0abBic2Envhej22DVXV9nCFaS5hIQbkyo1AdGb+gNME2TSh3hYJVV/w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.12.1",
+ "@babel/helper-replace-supers": "^7.12.1",
+ "@babel/helper-simple-access": "^7.12.1",
+ "@babel/helper-split-export-declaration": "^7.11.0",
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "@babel/template": "^7.10.4",
+ "@babel/traverse": "^7.12.1",
+ "@babel/types": "^7.12.1",
+ "lodash": "^4.17.19"
+ },
+ "dependencies": {
+ "@babel/generator": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.1.tgz",
+ "integrity": "sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.12.1",
+ "jsesc": "^2.5.1",
+ "source-map": "^0.5.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.12.3",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.3.tgz",
+ "integrity": "sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw==",
+ "dev": true
+ },
+ "@babel/traverse": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.1.tgz",
+ "integrity": "sha512-MA3WPoRt1ZHo2ZmoGKNqi20YnPt0B1S0GTZEPhhd+hw2KGUzBlHuVunj6K4sNuK+reEvyiPwtp0cpaqLzJDmAw==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/generator": "^7.12.1",
+ "@babel/helper-function-name": "^7.10.4",
+ "@babel/helper-split-export-declaration": "^7.11.0",
+ "@babel/parser": "^7.12.1",
+ "@babel/types": "^7.12.1",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0",
+ "lodash": "^4.17.19"
+ }
+ },
+ "@babel/types": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz",
+ "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
+ "@babel/helper-optimise-call-expression": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz",
+ "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helper-plugin-utils": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz",
+ "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==",
+ "dev": true
+ },
+ "@babel/helper-regex": {
+ "version": "7.10.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz",
+ "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.19"
+ }
+ },
+ "@babel/helper-remap-async-to-generator": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.12.1.tgz",
+ "integrity": "sha512-9d0KQCRM8clMPcDwo8SevNs+/9a8yWVVmaE80FGJcEP8N1qToREmWEGnBn8BUlJhYRFz6fqxeRL1sl5Ogsed7A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.10.4",
+ "@babel/helper-wrap-function": "^7.10.4",
+ "@babel/types": "^7.12.1"
+ },
+ "dependencies": {
+ "@babel/types": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz",
+ "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ }
+ }
+ },
+ "@babel/helper-replace-supers": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.12.1.tgz",
+ "integrity": "sha512-zJjTvtNJnCFsCXVi5rUInstLd/EIVNmIKA1Q9ynESmMBWPWd+7sdR+G4/wdu+Mppfep0XLyG2m7EBPvjCeFyrw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-member-expression-to-functions": "^7.12.1",
+ "@babel/helper-optimise-call-expression": "^7.10.4",
+ "@babel/traverse": "^7.12.1",
+ "@babel/types": "^7.12.1"
+ },
+ "dependencies": {
+ "@babel/generator": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.1.tgz",
+ "integrity": "sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.12.1",
+ "jsesc": "^2.5.1",
+ "source-map": "^0.5.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.12.3",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.3.tgz",
+ "integrity": "sha512-kFsOS0IbsuhO5ojF8Hc8z/8vEIOkylVBrjiZUbLTE3XFe0Qi+uu6HjzQixkFaqr0ZPAMZcBVxEwmsnsLPZ2Xsw==",
+ "dev": true
+ },
+ "@babel/traverse": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.1.tgz",
+ "integrity": "sha512-MA3WPoRt1ZHo2ZmoGKNqi20YnPt0B1S0GTZEPhhd+hw2KGUzBlHuVunj6K4sNuK+reEvyiPwtp0cpaqLzJDmAw==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/generator": "^7.12.1",
+ "@babel/helper-function-name": "^7.10.4",
+ "@babel/helper-split-export-declaration": "^7.11.0",
+ "@babel/parser": "^7.12.1",
+ "@babel/types": "^7.12.1",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0",
+ "lodash": "^4.17.19"
+ }
+ },
+ "@babel/types": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz",
+ "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
+ "@babel/helper-simple-access": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.12.1.tgz",
+ "integrity": "sha512-OxBp7pMrjVewSSC8fXDFrHrBcJATOOFssZwv16F3/6Xtc138GHybBfPbm9kfiqQHKhYQrlamWILwlDCeyMFEaA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.12.1"
+ },
+ "dependencies": {
+ "@babel/types": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz",
+ "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ }
+ }
+ },
+ "@babel/helper-skip-transparent-expression-wrappers": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz",
+ "integrity": "sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA==",
+ "dev": true,
+ "requires": {
+ "@babel/types": "^7.12.1"
+ },
+ "dependencies": {
+ "@babel/types": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.1.tgz",
+ "integrity": "sha512-BzSY3NJBKM4kyatSOWh3D/JJ2O3CVzBybHWxtgxnggaxEuaSTTDqeiSb/xk9lrkw2Tbqyivw5ZU4rT+EfznQsA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ }
+ }
+ },
+ "@babel/helper-split-export-declaration": {
+ "version": "7.11.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
+ "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==",
+ "requires": {
+ "@babel/types": "^7.11.0"
+ }
+ },
+ "@babel/helper-validator-identifier": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+ "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw=="
+ },
+ "@babel/helper-validator-option": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.1.tgz",
+ "integrity": "sha512-YpJabsXlJVWP0USHjnC/AQDTLlZERbON577YUVO/wLpqyj6HAtVYnWaQaN0iUN+1/tWn3c+uKKXjRut5115Y2A==",
+ "dev": true
+ },
+ "@babel/helper-wrap-function": {
+ "version": "7.12.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.12.3.tgz",
+ "integrity": "sha512-Cvb8IuJDln3rs6tzjW3Y8UeelAOdnpB8xtQ4sme2MSZ9wOxrbThporC0y/EtE16VAtoyEfLM404Xr1e0OOp+ow==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.10.4",
+ "@babel/template": "^7.10.4",
+ "@babel/traverse": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/helpers": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz",
+ "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==",
+ "requires": {
+ "@babel/template": "^7.10.4",
+ "@babel/traverse": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+ "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "@babel/parser": {
+ "version": "7.11.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz",
+ "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q=="
+ },
+ "@babel/plugin-proposal-async-generator-functions": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.1.tgz",
+ "integrity": "sha512-d+/o30tJxFxrA1lhzJqiUcEJdI6jKlNregCv5bASeGf2Q4MXmnwH7viDo7nhx1/ohf09oaH8j1GVYG/e3Yqk6A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/helper-remap-async-to-generator": "^7.12.1",
+ "@babel/plugin-syntax-async-generators": "^7.8.0"
+ }
+ },
+ "@babel/plugin-proposal-class-properties": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.12.1.tgz",
+ "integrity": "sha512-cKp3dlQsFsEs5CWKnN7BnSHOd0EOW8EKpEjkoz1pO2E5KzIDNV9Ros1b0CnmbVgAGXJubOYVBOGCT1OmJwOI7w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-proposal-dynamic-import": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.12.1.tgz",
+ "integrity": "sha512-a4rhUSZFuq5W8/OO8H7BL5zspjnc1FLd9hlOxIK/f7qG4a0qsqk8uvF/ywgBA8/OmjsapjpvaEOYItfGG1qIvQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/plugin-syntax-dynamic-import": "^7.8.0"
+ }
+ },
+ "@babel/plugin-proposal-export-namespace-from": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.1.tgz",
+ "integrity": "sha512-6CThGf0irEkzujYS5LQcjBx8j/4aQGiVv7J9+2f7pGfxqyKh3WnmVJYW3hdrQjyksErMGBPQrCnHfOtna+WLbw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/plugin-syntax-export-namespace-from": "^7.8.3"
+ }
+ },
+ "@babel/plugin-proposal-json-strings": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.12.1.tgz",
+ "integrity": "sha512-GoLDUi6U9ZLzlSda2Df++VSqDJg3CG+dR0+iWsv6XRw1rEq+zwt4DirM9yrxW6XWaTpmai1cWJLMfM8qQJf+yw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/plugin-syntax-json-strings": "^7.8.0"
+ }
+ },
+ "@babel/plugin-proposal-logical-assignment-operators": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.12.1.tgz",
+ "integrity": "sha512-k8ZmVv0JU+4gcUGeCDZOGd0lCIamU/sMtIiX3UWnUc5yzgq6YUGyEolNYD+MLYKfSzgECPcqetVcJP9Afe/aCA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
+ }
+ },
+ "@babel/plugin-proposal-nullish-coalescing-operator": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.12.1.tgz",
+ "integrity": "sha512-nZY0ESiaQDI1y96+jk6VxMOaL4LPo/QDHBqL+SF3/vl6dHkTwHlOI8L4ZwuRBHgakRBw5zsVylel7QPbbGuYgg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0"
+ }
+ },
+ "@babel/plugin-proposal-numeric-separator": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.1.tgz",
+ "integrity": "sha512-MR7Ok+Af3OhNTCxYVjJZHS0t97ydnJZt/DbR4WISO39iDnhiD8XHrY12xuSJ90FFEGjir0Fzyyn7g/zY6hxbxA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/plugin-syntax-numeric-separator": "^7.10.4"
+ }
+ },
+ "@babel/plugin-proposal-object-rest-spread": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.12.1.tgz",
+ "integrity": "sha512-s6SowJIjzlhx8o7lsFx5zmY4At6CTtDvgNQDdPzkBQucle58A6b/TTeEBYtyDgmcXjUTM+vE8YOGHZzzbc/ioA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.0",
+ "@babel/plugin-transform-parameters": "^7.12.1"
+ }
+ },
+ "@babel/plugin-proposal-optional-catch-binding": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.12.1.tgz",
+ "integrity": "sha512-hFvIjgprh9mMw5v42sJWLI1lzU5L2sznP805zeT6rySVRA0Y18StRhDqhSxlap0oVgItRsB6WSROp4YnJTJz0g==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.0"
+ }
+ },
+ "@babel/plugin-proposal-optional-chaining": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.12.1.tgz",
+ "integrity": "sha512-c2uRpY6WzaVDzynVY9liyykS+kVU+WRZPMPYpkelXH8KBt1oXoI89kPbZKKG/jDT5UK92FTW2fZkZaJhdiBabw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.0"
+ }
+ },
+ "@babel/plugin-proposal-private-methods": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.12.1.tgz",
+ "integrity": "sha512-mwZ1phvH7/NHK6Kf8LP7MYDogGV+DKB1mryFOEwx5EBNQrosvIczzZFTUmWaeujd5xT6G1ELYWUz3CutMhjE1w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-class-features-plugin": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-proposal-unicode-property-regex": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.1.tgz",
+ "integrity": "sha512-MYq+l+PvHuw/rKUz1at/vb6nCnQ2gmJBNaM62z0OgH7B2W1D9pvkpYtlti9bGtizNIU1K3zm4bZF9F91efVY0w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-syntax-async-generators": {
+ "version": "7.8.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
+ "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-class-properties": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.1.tgz",
+ "integrity": "sha512-U40A76x5gTwmESz+qiqssqmeEsKvcSyvtgktrm0uzcARAmM9I1jR221f6Oq+GmHrcD+LvZDag1UTOTe2fL3TeA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-syntax-dynamic-import": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz",
+ "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-export-namespace-from": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz",
+ "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ }
+ },
+ "@babel/plugin-syntax-json-strings": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
+ "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-logical-assignment-operators": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
+ "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-syntax-nullish-coalescing-operator": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
+ "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-numeric-separator": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
+ "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-syntax-object-rest-spread": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
+ "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-optional-catch-binding": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
+ "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-optional-chaining": {
+ "version": "7.8.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
+ "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ }
+ },
+ "@babel/plugin-syntax-top-level-await": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.1.tgz",
+ "integrity": "sha512-i7ooMZFS+a/Om0crxZodrTzNEPJHZrlMVGMTEpFAj6rYY/bKCddB0Dk/YxfPuYXOopuhKk/e1jV6h+WUU9XN3A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-arrow-functions": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.12.1.tgz",
+ "integrity": "sha512-5QB50qyN44fzzz4/qxDPQMBCTHgxg3n0xRBLJUmBlLoU/sFvxVWGZF/ZUfMVDQuJUKXaBhbupxIzIfZ6Fwk/0A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-async-to-generator": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.12.1.tgz",
+ "integrity": "sha512-SDtqoEcarK1DFlRJ1hHRY5HvJUj5kX4qmtpMAm2QnhOlyuMC4TMdCRgW6WXpv93rZeYNeLP22y8Aq2dbcDRM1A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/helper-remap-async-to-generator": "^7.12.1"
+ }
+ },
+ "@babel/plugin-transform-block-scoped-functions": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.1.tgz",
+ "integrity": "sha512-5OpxfuYnSgPalRpo8EWGPzIYf0lHBWORCkj5M0oLBwHdlux9Ri36QqGW3/LR13RSVOAoUUMzoPI/jpE4ABcHoA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-block-scoping": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.1.tgz",
+ "integrity": "sha512-zJyAC9sZdE60r1nVQHblcfCj29Dh2Y0DOvlMkcqSo0ckqjiCwNiUezUKw+RjOCwGfpLRwnAeQ2XlLpsnGkvv9w==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-classes": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.12.1.tgz",
+ "integrity": "sha512-/74xkA7bVdzQTBeSUhLLJgYIcxw/dpEpCdRDiHgPJ3Mv6uC11UhjpOhl72CgqbBCmt1qtssCyB2xnJm1+PFjog==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-annotate-as-pure": "^7.10.4",
+ "@babel/helper-define-map": "^7.10.4",
+ "@babel/helper-function-name": "^7.10.4",
+ "@babel/helper-optimise-call-expression": "^7.10.4",
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/helper-replace-supers": "^7.12.1",
+ "@babel/helper-split-export-declaration": "^7.10.4",
+ "globals": "^11.1.0"
+ }
+ },
+ "@babel/plugin-transform-computed-properties": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.12.1.tgz",
+ "integrity": "sha512-vVUOYpPWB7BkgUWPo4C44mUQHpTZXakEqFjbv8rQMg7TC6S6ZhGZ3otQcRH6u7+adSlE5i0sp63eMC/XGffrzg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-destructuring": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.12.1.tgz",
+ "integrity": "sha512-fRMYFKuzi/rSiYb2uRLiUENJOKq4Gnl+6qOv5f8z0TZXg3llUwUhsNNwrwaT/6dUhJTzNpBr+CUvEWBtfNY1cw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-dotall-regex": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.1.tgz",
+ "integrity": "sha512-B2pXeRKoLszfEW7J4Hg9LoFaWEbr/kzo3teWHmtFCszjRNa/b40f9mfeqZsIDLLt/FjwQ6pz/Gdlwy85xNckBA==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-duplicate-keys": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.1.tgz",
+ "integrity": "sha512-iRght0T0HztAb/CazveUpUQrZY+aGKKaWXMJ4uf9YJtqxSUe09j3wteztCUDRHs+SRAL7yMuFqUsLoAKKzgXjw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-exponentiation-operator": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.1.tgz",
+ "integrity": "sha512-7tqwy2bv48q+c1EHbXK0Zx3KXd2RVQp6OC7PbwFNt/dPTAV3Lu5sWtWuAj8owr5wqtWnqHfl2/mJlUmqkChKug==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4",
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-for-of": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.12.1.tgz",
+ "integrity": "sha512-Zaeq10naAsuHo7heQvyV0ptj4dlZJwZgNAtBYBnu5nNKJoW62m0zKcIEyVECrUKErkUkg6ajMy4ZfnVZciSBhg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-function-name": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.1.tgz",
+ "integrity": "sha512-JF3UgJUILoFrFMEnOJLJkRHSk6LUSXLmEFsA23aR2O5CSLUxbeUX1IZ1YQ7Sn0aXb601Ncwjx73a+FVqgcljVw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-function-name": "^7.10.4",
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-literals": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.1.tgz",
+ "integrity": "sha512-+PxVGA+2Ag6uGgL0A5f+9rklOnnMccwEBzwYFL3EUaKuiyVnUipyXncFcfjSkbimLrODoqki1U9XxZzTvfN7IQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-member-expression-literals": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.1.tgz",
+ "integrity": "sha512-1sxePl6z9ad0gFMB9KqmYofk34flq62aqMt9NqliS/7hPEpURUCMbyHXrMPlo282iY7nAvUB1aQd5mg79UD9Jg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-modules-amd": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.12.1.tgz",
+ "integrity": "sha512-tDW8hMkzad5oDtzsB70HIQQRBiTKrhfgwC/KkJeGsaNFTdWhKNt/BiE8c5yj19XiGyrxpbkOfH87qkNg1YGlOQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "babel-plugin-dynamic-import-node": "^2.3.3"
+ }
+ },
+ "@babel/plugin-transform-modules-commonjs": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.12.1.tgz",
+ "integrity": "sha512-dY789wq6l0uLY8py9c1B48V8mVL5gZh/+PQ5ZPrylPYsnAvnEMjqsUXkuoDVPeVK+0VyGar+D08107LzDQ6pag==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/helper-simple-access": "^7.12.1",
+ "babel-plugin-dynamic-import-node": "^2.3.3"
+ }
+ },
+ "@babel/plugin-transform-modules-systemjs": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.12.1.tgz",
+ "integrity": "sha512-Hn7cVvOavVh8yvW6fLwveFqSnd7rbQN3zJvoPNyNaQSvgfKmDBO9U1YL9+PCXGRlZD9tNdWTy5ACKqMuzyn32Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-hoist-variables": "^7.10.4",
+ "@babel/helper-module-transforms": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "babel-plugin-dynamic-import-node": "^2.3.3"
+ }
+ },
+ "@babel/plugin-transform-modules-umd": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.12.1.tgz",
+ "integrity": "sha512-aEIubCS0KHKM0zUos5fIoQm+AZUMt1ZvMpqz0/H5qAQ7vWylr9+PLYurT+Ic7ID/bKLd4q8hDovaG3Zch2uz5Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-transforms": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-named-capturing-groups-regex": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.1.tgz",
+ "integrity": "sha512-tB43uQ62RHcoDp9v2Nsf+dSM8sbNodbEicbQNA53zHz8pWUhsgHSJCGpt7daXxRydjb0KnfmB+ChXOv3oADp1Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.12.1"
+ }
+ },
+ "@babel/plugin-transform-new-target": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.1.tgz",
+ "integrity": "sha512-+eW/VLcUL5L9IvJH7rT1sT0CzkdUTvPrXC2PXTn/7z7tXLBuKvezYbGdxD5WMRoyvyaujOq2fWoKl869heKjhw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-object-super": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.1.tgz",
+ "integrity": "sha512-AvypiGJH9hsquNUn+RXVcBdeE3KHPZexWRdimhuV59cSoOt5kFBmqlByorAeUlGG2CJWd0U+4ZtNKga/TB0cAw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/helper-replace-supers": "^7.12.1"
+ }
+ },
+ "@babel/plugin-transform-parameters": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.12.1.tgz",
+ "integrity": "sha512-xq9C5EQhdPK23ZeCdMxl8bbRnAgHFrw5EOC3KJUsSylZqdkCaFEXxGSBuTSObOpiiHHNyb82es8M1QYgfQGfNg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-property-literals": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.1.tgz",
+ "integrity": "sha512-6MTCR/mZ1MQS+AwZLplX4cEySjCpnIF26ToWo942nqn8hXSm7McaHQNeGx/pt7suI1TWOWMfa/NgBhiqSnX0cQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-regenerator": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.1.tgz",
+ "integrity": "sha512-gYrHqs5itw6i4PflFX3OdBPMQdPbF4bj2REIUxlMRUFk0/ZOAIpDFuViuxPjUL7YC8UPnf+XG7/utJvqXdPKng==",
+ "dev": true,
+ "requires": {
+ "regenerator-transform": "^0.14.2"
+ }
+ },
+ "@babel/plugin-transform-reserved-words": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.1.tgz",
+ "integrity": "sha512-pOnUfhyPKvZpVyBHhSBoX8vfA09b7r00Pmm1sH+29ae2hMTKVmSp4Ztsr8KBKjLjx17H0eJqaRC3bR2iThM54A==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-runtime": {
+ "version": "7.11.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.11.0.tgz",
+ "integrity": "sha512-LFEsP+t3wkYBlis8w6/kmnd6Kb1dxTd+wGJ8MlxTGzQo//ehtqlVL4S9DNUa53+dtPSQobN2CXx4d81FqC58cw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-module-imports": "^7.10.4",
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "resolve": "^1.8.1",
+ "semver": "^5.5.1"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/plugin-transform-shorthand-properties": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.1.tgz",
+ "integrity": "sha512-GFZS3c/MhX1OusqB1MZ1ct2xRzX5ppQh2JU1h2Pnfk88HtFTM+TWQqJNfwkmxtPQtb/s1tk87oENfXJlx7rSDw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-spread": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.12.1.tgz",
+ "integrity": "sha512-vuLp8CP0BE18zVYjsEBZ5xoCecMK6LBMMxYzJnh01rxQRvhNhH1csMMmBfNo5tGpGO+NhdSNW2mzIvBu3K1fng==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.12.1"
+ }
+ },
+ "@babel/plugin-transform-sticky-regex": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.1.tgz",
+ "integrity": "sha512-CiUgKQ3AGVk7kveIaPEET1jNDhZZEl1RPMWdTBE1799bdz++SwqDHStmxfCtDfBhQgCl38YRiSnrMuUMZIWSUQ==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/helper-regex": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-template-literals": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.12.1.tgz",
+ "integrity": "sha512-b4Zx3KHi+taXB1dVRBhVJtEPi9h1THCeKmae2qP0YdUHIFhVjtpqqNfxeVAa1xeHVhAy4SbHxEwx5cltAu5apw==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-typeof-symbol": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.1.tgz",
+ "integrity": "sha512-EPGgpGy+O5Kg5pJFNDKuxt9RdmTgj5sgrus2XVeMp/ZIbOESadgILUbm50SNpghOh3/6yrbsH+NB5+WJTmsA7Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-unicode-escapes": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.1.tgz",
+ "integrity": "sha512-I8gNHJLIc7GdApm7wkVnStWssPNbSRMPtgHdmH3sRM1zopz09UWPS4x5V4n1yz/MIWTVnJ9sp6IkuXdWM4w+2Q==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/plugin-transform-unicode-regex": {
+ "version": "7.12.1",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.1.tgz",
+ "integrity": "sha512-SqH4ClNngh/zGwHZOOQMTD+e8FGWexILV+ePMyiDJttAWRh5dhDL8rcl5lSgU3Huiq6Zn6pWTMvdPAb21Dwdyg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-create-regexp-features-plugin": "^7.12.1",
+ "@babel/helper-plugin-utils": "^7.10.4"
+ }
+ },
+ "@babel/preset-env": {
+ "version": "7.11.0",
+ "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.0.tgz",
+ "integrity": "sha512-2u1/k7rG/gTh02dylX2kL3S0IJNF+J6bfDSp4DI2Ma8QN6Y9x9pmAax59fsCk6QUQG0yqH47yJWA+u1I1LccAg==",
+ "dev": true,
+ "requires": {
+ "@babel/compat-data": "^7.11.0",
+ "@babel/helper-compilation-targets": "^7.10.4",
+ "@babel/helper-module-imports": "^7.10.4",
+ "@babel/helper-plugin-utils": "^7.10.4",
+ "@babel/plugin-proposal-async-generator-functions": "^7.10.4",
+ "@babel/plugin-proposal-class-properties": "^7.10.4",
+ "@babel/plugin-proposal-dynamic-import": "^7.10.4",
+ "@babel/plugin-proposal-export-namespace-from": "^7.10.4",
+ "@babel/plugin-proposal-json-strings": "^7.10.4",
+ "@babel/plugin-proposal-logical-assignment-operators": "^7.11.0",
+ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4",
+ "@babel/plugin-proposal-numeric-separator": "^7.10.4",
+ "@babel/plugin-proposal-object-rest-spread": "^7.11.0",
+ "@babel/plugin-proposal-optional-catch-binding": "^7.10.4",
+ "@babel/plugin-proposal-optional-chaining": "^7.11.0",
+ "@babel/plugin-proposal-private-methods": "^7.10.4",
+ "@babel/plugin-proposal-unicode-property-regex": "^7.10.4",
+ "@babel/plugin-syntax-async-generators": "^7.8.0",
+ "@babel/plugin-syntax-class-properties": "^7.10.4",
+ "@babel/plugin-syntax-dynamic-import": "^7.8.0",
+ "@babel/plugin-syntax-export-namespace-from": "^7.8.3",
+ "@babel/plugin-syntax-json-strings": "^7.8.0",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0",
+ "@babel/plugin-syntax-numeric-separator": "^7.10.4",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.0",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.0",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.0",
+ "@babel/plugin-syntax-top-level-await": "^7.10.4",
+ "@babel/plugin-transform-arrow-functions": "^7.10.4",
+ "@babel/plugin-transform-async-to-generator": "^7.10.4",
+ "@babel/plugin-transform-block-scoped-functions": "^7.10.4",
+ "@babel/plugin-transform-block-scoping": "^7.10.4",
+ "@babel/plugin-transform-classes": "^7.10.4",
+ "@babel/plugin-transform-computed-properties": "^7.10.4",
+ "@babel/plugin-transform-destructuring": "^7.10.4",
+ "@babel/plugin-transform-dotall-regex": "^7.10.4",
+ "@babel/plugin-transform-duplicate-keys": "^7.10.4",
+ "@babel/plugin-transform-exponentiation-operator": "^7.10.4",
+ "@babel/plugin-transform-for-of": "^7.10.4",
+ "@babel/plugin-transform-function-name": "^7.10.4",
+ "@babel/plugin-transform-literals": "^7.10.4",
+ "@babel/plugin-transform-member-expression-literals": "^7.10.4",
+ "@babel/plugin-transform-modules-amd": "^7.10.4",
+ "@babel/plugin-transform-modules-commonjs": "^7.10.4",
+ "@babel/plugin-transform-modules-systemjs": "^7.10.4",
+ "@babel/plugin-transform-modules-umd": "^7.10.4",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.10.4",
+ "@babel/plugin-transform-new-target": "^7.10.4",
+ "@babel/plugin-transform-object-super": "^7.10.4",
+ "@babel/plugin-transform-parameters": "^7.10.4",
+ "@babel/plugin-transform-property-literals": "^7.10.4",
+ "@babel/plugin-transform-regenerator": "^7.10.4",
+ "@babel/plugin-transform-reserved-words": "^7.10.4",
+ "@babel/plugin-transform-shorthand-properties": "^7.10.4",
+ "@babel/plugin-transform-spread": "^7.11.0",
+ "@babel/plugin-transform-sticky-regex": "^7.10.4",
+ "@babel/plugin-transform-template-literals": "^7.10.4",
+ "@babel/plugin-transform-typeof-symbol": "^7.10.4",
+ "@babel/plugin-transform-unicode-escapes": "^7.10.4",
+ "@babel/plugin-transform-unicode-regex": "^7.10.4",
+ "@babel/preset-modules": "^0.1.3",
+ "@babel/types": "^7.11.0",
+ "browserslist": "^4.12.0",
+ "core-js-compat": "^3.6.2",
+ "invariant": "^2.2.2",
+ "levenary": "^1.1.1",
+ "semver": "^5.5.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "@babel/preset-modules": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.4.tgz",
+ "integrity": "sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg==",
+ "dev": true,
+ "requires": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-proposal-unicode-property-regex": "^7.4.4",
+ "@babel/plugin-transform-dotall-regex": "^7.4.4",
+ "@babel/types": "^7.4.4",
+ "esutils": "^2.0.2"
+ }
+ },
+ "@babel/runtime": {
+ "version": "7.11.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz",
+ "integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==",
+ "dev": true,
+ "requires": {
+ "regenerator-runtime": "^0.13.4"
+ }
+ },
+ "@babel/template": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
+ "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/parser": "^7.10.4",
+ "@babel/types": "^7.10.4"
+ }
+ },
+ "@babel/traverse": {
+ "version": "7.11.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz",
+ "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==",
+ "requires": {
+ "@babel/code-frame": "^7.10.4",
+ "@babel/generator": "^7.11.5",
+ "@babel/helper-function-name": "^7.10.4",
+ "@babel/helper-split-export-declaration": "^7.11.0",
+ "@babel/parser": "^7.11.5",
+ "@babel/types": "^7.11.5",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0",
+ "lodash": "^4.17.19"
+ },
+ "dependencies": {
+ "@babel/generator": {
+ "version": "7.11.6",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz",
+ "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==",
+ "requires": {
+ "@babel/types": "^7.11.5",
+ "jsesc": "^2.5.1",
+ "source-map": "^0.5.0"
+ }
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
+ }
+ }
+ },
+ "@babel/types": {
+ "version": "7.11.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
+ "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
+ "requires": {
+ "@babel/helper-validator-identifier": "^7.10.4",
+ "lodash": "^4.17.19",
+ "to-fast-properties": "^2.0.0"
+ }
+ },
+ "@istanbuljs/schema": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz",
+ "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==",
+ "dev": true
+ },
+ "@jsdevtools/coverage-istanbul-loader": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz",
+ "integrity": "sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA==",
+ "dev": true,
+ "requires": {
+ "convert-source-map": "^1.7.0",
+ "istanbul-lib-instrument": "^4.0.3",
+ "loader-utils": "^2.0.0",
+ "merge-source-map": "^1.1.0",
+ "schema-utils": "^2.7.0"
+ }
+ },
+ "@ng-bootstrap/ng-bootstrap": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-8.0.0.tgz",
+ "integrity": "sha512-v77Gfd8xHH+exq0WqIqVRlxbUEHdA/2+RUJenUP2IDTQN9E1rWl7O461/kosr+0XPuxPArHQJxhh/WsCYckcNg==",
+ "requires": {
+ "tslib": "^2.0.0"
+ }
+ },
+ "@ngtools/webpack": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-10.2.0.tgz",
+ "integrity": "sha512-W4SSFNQhIiC8JRhIn3c4mb1+fsFKiHp+THVMAUNo+wRZEt/rgzsCdnqv0EmQJJojZhnilUIyB/wVYJu2+S/Bxg==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/core": "10.2.0",
+ "enhanced-resolve": "4.3.0",
+ "webpack-sources": "1.4.3"
+ },
+ "dependencies": {
+ "@angular-devkit/core": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-10.2.0.tgz",
+ "integrity": "sha512-XAszFhSF3mZw1VjoOsYGbArr5NJLcStjOvcCGjBPl1UBM2AKpuCQXHxI9XJGYKL3B93Vp5G58d8qkHvamT53OA==",
+ "dev": true,
+ "requires": {
+ "ajv": "6.12.4",
+ "fast-json-stable-stringify": "2.1.0",
+ "magic-string": "0.25.7",
+ "rxjs": "6.6.2",
+ "source-map": "0.7.3"
+ }
+ },
+ "rxjs": {
+ "version": "6.6.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz",
+ "integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
+ }
+ },
+ "@nodelib/fs.scandir": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz",
+ "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "2.0.3",
+ "run-parallel": "^1.1.9"
+ }
+ },
+ "@nodelib/fs.stat": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz",
+ "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==",
+ "dev": true
+ },
+ "@nodelib/fs.walk": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz",
+ "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.scandir": "2.1.3",
+ "fastq": "^1.6.0"
+ }
+ },
+ "@npmcli/move-file": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz",
+ "integrity": "sha512-Uv6h1sT+0DrblvIrolFtbvM1FgWm+/sy4B3pvLp67Zys+thcukzS5ekn7HsZFGpWP4Q3fYJCljbWQE/XivMRLw==",
+ "dev": true,
+ "requires": {
+ "mkdirp": "^1.0.4"
+ },
+ "dependencies": {
+ "mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true
+ }
+ }
+ },
+ "@scarf/scarf": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.1.0.tgz",
+ "integrity": "sha512-b2iE8kjjzzUo2WZ0xuE2N77kfnTds7ClrDxcz3Atz7h2XrNVoAPUoT75i7CY0st5x++70V91Y+c6RpBX9MX7Jg=="
+ },
+ "@schematics/angular": {
+ "version": "10.1.5",
+ "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-10.1.5.tgz",
+ "integrity": "sha512-3VRcMB9WpjcMvlZ1y+78WGuZ4Ehp9pGw/T+zAR1VG9/16XHDQyfObsMuaU2EnEoufiHbTe3UpvVpYOu6tOCJrA==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/core": "10.1.5",
+ "@angular-devkit/schematics": "10.1.5",
+ "jsonc-parser": "2.3.0"
+ }
+ },
+ "@schematics/update": {
+ "version": "0.1001.5",
+ "resolved": "https://registry.npmjs.org/@schematics/update/-/update-0.1001.5.tgz",
+ "integrity": "sha512-DSomJ5IMs/5HUPx0RdPYubPWXh7kToxXUZbJywe0Q+TWTd+1xFfg8++O1DG4iW7E/Boqojx5VenAOzWY9jDWjA==",
+ "dev": true,
+ "requires": {
+ "@angular-devkit/core": "10.1.5",
+ "@angular-devkit/schematics": "10.1.5",
+ "@yarnpkg/lockfile": "1.1.0",
+ "ini": "1.3.5",
+ "npm-package-arg": "^8.0.0",
+ "pacote": "9.5.12",
+ "semver": "7.3.2",
+ "semver-intersect": "1.4.0"
+ }
+ },
+ "@types/glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==",
+ "dev": true,
+ "requires": {
+ "@types/minimatch": "*",
+ "@types/node": "*"
+ }
+ },
+ "@types/jasmine": {
+ "version": "3.5.14",
+ "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-3.5.14.tgz",
+ "integrity": "sha512-Fkgk536sHPqcOtd+Ow+WiUNuk0TSo/BntKkF8wSvcd6M2FvPjeXcUE6Oz/bwDZiUZEaXLslAgw00Q94Pnx6T4w==",
+ "dev": true
+ },
+ "@types/jasminewd2": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.8.tgz",
+ "integrity": "sha512-d9p31r7Nxk0ZH0U39PTH0hiDlJ+qNVGjlt1ucOoTUptxb2v+Y5VMnsxfwN+i3hK4yQnqBi3FMmoMFcd1JHDxdg==",
+ "dev": true,
+ "requires": {
+ "@types/jasmine": "*"
+ }
+ },
+ "@types/json-schema": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz",
+ "integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
+ "dev": true
+ },
+ "@types/minimatch": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
+ "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==",
+ "dev": true
+ },
+ "@types/node": {
+ "version": "12.12.64",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.64.tgz",
+ "integrity": "sha512-UV1/ZJMC+HcP902wWdpC43cAcGu0IQk/I5bXjP2aSuCjsk3cE74mDvFrLKga7oDC170ugOAYBwfT4DSQW3akDA==",
+ "dev": true
+ },
+ "@types/pdfjs-dist": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@types/pdfjs-dist/-/pdfjs-dist-2.1.7.tgz",
+ "integrity": "sha512-nQIwcPUhkAIyn7x9NS0lR/qxYfd5unRtfGkMjvpgF4Sh28IXftRymaNmFKTTdejDNY25NDGSIyjwj/BRwAPexg=="
+ },
+ "@types/q": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz",
+ "integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==",
+ "dev": true
+ },
+ "@types/selenium-webdriver": {
+ "version": "3.0.17",
+ "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.17.tgz",
+ "integrity": "sha512-tGomyEuzSC1H28y2zlW6XPCaDaXFaD6soTdb4GNdmte2qfHtrKqhy0ZFs4r/1hpazCfEZqeTSRLvSasmEx89uw==",
+ "dev": true
+ },
+ "@types/source-list-map": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz",
+ "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==",
+ "dev": true
+ },
+ "@types/webpack-sources": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.8.tgz",
+ "integrity": "sha512-JHB2/xZlXOjzjBB6fMOpH1eQAfsrpqVVIbneE0Rok16WXwFaznaI5vfg75U5WgGJm7V9W1c4xeRQDjX/zwvghA==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*",
+ "@types/source-list-map": "*",
+ "source-map": "^0.6.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "@webassemblyjs/ast": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
+ "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/helper-module-context": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/wast-parser": "1.9.0"
+ }
+ },
+ "@webassemblyjs/floating-point-hex-parser": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz",
+ "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-api-error": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz",
+ "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-buffer": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz",
+ "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-code-frame": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz",
+ "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/wast-printer": "1.9.0"
+ }
+ },
+ "@webassemblyjs/helper-fsm": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz",
+ "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-module-context": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz",
+ "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.9.0"
+ }
+ },
+ "@webassemblyjs/helper-wasm-bytecode": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz",
+ "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==",
+ "dev": true
+ },
+ "@webassemblyjs/helper-wasm-section": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz",
+ "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-buffer": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/wasm-gen": "1.9.0"
+ }
+ },
+ "@webassemblyjs/ieee754": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz",
+ "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==",
+ "dev": true,
+ "requires": {
+ "@xtuc/ieee754": "^1.2.0"
+ }
+ },
+ "@webassemblyjs/leb128": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz",
+ "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==",
+ "dev": true,
+ "requires": {
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "@webassemblyjs/utf8": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz",
+ "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==",
+ "dev": true
+ },
+ "@webassemblyjs/wasm-edit": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz",
+ "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-buffer": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/helper-wasm-section": "1.9.0",
+ "@webassemblyjs/wasm-gen": "1.9.0",
+ "@webassemblyjs/wasm-opt": "1.9.0",
+ "@webassemblyjs/wasm-parser": "1.9.0",
+ "@webassemblyjs/wast-printer": "1.9.0"
+ }
+ },
+ "@webassemblyjs/wasm-gen": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz",
+ "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/ieee754": "1.9.0",
+ "@webassemblyjs/leb128": "1.9.0",
+ "@webassemblyjs/utf8": "1.9.0"
+ }
+ },
+ "@webassemblyjs/wasm-opt": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz",
+ "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-buffer": "1.9.0",
+ "@webassemblyjs/wasm-gen": "1.9.0",
+ "@webassemblyjs/wasm-parser": "1.9.0"
+ }
+ },
+ "@webassemblyjs/wasm-parser": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz",
+ "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-api-error": "1.9.0",
+ "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
+ "@webassemblyjs/ieee754": "1.9.0",
+ "@webassemblyjs/leb128": "1.9.0",
+ "@webassemblyjs/utf8": "1.9.0"
+ }
+ },
+ "@webassemblyjs/wast-parser": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz",
+ "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/floating-point-hex-parser": "1.9.0",
+ "@webassemblyjs/helper-api-error": "1.9.0",
+ "@webassemblyjs/helper-code-frame": "1.9.0",
+ "@webassemblyjs/helper-fsm": "1.9.0",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "@webassemblyjs/wast-printer": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz",
+ "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/wast-parser": "1.9.0",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "@xtuc/ieee754": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
+ "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
+ "dev": true
+ },
+ "@xtuc/long": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
+ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
+ "dev": true
+ },
+ "@yarnpkg/lockfile": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
+ "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
+ "dev": true
+ },
+ "JSONStream": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
+ "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+ "dev": true,
+ "requires": {
+ "jsonparse": "^1.2.0",
+ "through": ">=2.2.7 <3"
+ }
+ },
+ "abab": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
+ "integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==",
+ "dev": true
+ },
+ "accepts": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
+ "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
+ "dev": true,
+ "requires": {
+ "mime-types": "~2.1.24",
+ "negotiator": "0.6.2"
+ }
+ },
+ "acorn": {
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
+ "integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==",
+ "dev": true
+ },
+ "adjust-sourcemap-loader": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/adjust-sourcemap-loader/-/adjust-sourcemap-loader-3.0.0.tgz",
+ "integrity": "sha512-YBrGyT2/uVQ/c6Rr+t6ZJXniY03YtHGMJQYal368burRGYKqhx9qGTWqcBU5s1CwYY9E/ri63RYyG1IacMZtqw==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^2.0.0",
+ "regex-parser": "^2.2.11"
+ }
+ },
+ "adm-zip": {
+ "version": "0.4.16",
+ "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz",
+ "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==",
+ "dev": true
+ },
+ "after": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/after/-/after-0.8.2.tgz",
+ "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=",
+ "dev": true
+ },
+ "agent-base": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
+ "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
+ "dev": true,
+ "requires": {
+ "es6-promisify": "^5.0.0"
+ }
+ },
+ "agentkeepalive": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-3.5.2.tgz",
+ "integrity": "sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ==",
+ "dev": true,
+ "requires": {
+ "humanize-ms": "^1.2.1"
+ }
+ },
+ "aggregate-error": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
+ "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
+ "dev": true,
+ "requires": {
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ }
+ },
+ "ajv": {
+ "version": "6.12.4",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz",
+ "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ajv-errors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
+ "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
+ "dev": true
+ },
+ "ajv-keywords": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+ "dev": true
+ },
+ "alphanum-sort": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz",
+ "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=",
+ "dev": true
+ },
+ "ansi-colors": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz",
+ "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==",
+ "dev": true
+ },
+ "ansi-escapes": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz",
+ "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==",
+ "dev": true,
+ "requires": {
+ "type-fest": "^0.11.0"
+ }
+ },
+ "ansi-html": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
+ "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "anymatch": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
+ "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "app-root-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-3.0.0.tgz",
+ "integrity": "sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw==",
+ "dev": true
+ },
+ "aproba": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
+ "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
+ "dev": true
+ },
+ "arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "aria-query": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz",
+ "integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=",
+ "dev": true,
+ "requires": {
+ "ast-types-flow": "0.0.7",
+ "commander": "^2.11.0"
+ }
+ },
+ "arity-n": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/arity-n/-/arity-n-1.0.4.tgz",
+ "integrity": "sha1-2edrEXM+CFacCEeuezmyhgswt0U=",
+ "dev": true
+ },
+ "arr-diff": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+ "dev": true
+ },
+ "arr-flatten": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+ "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+ "dev": true
+ },
+ "arr-union": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+ "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+ "dev": true
+ },
+ "array-flatten": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz",
+ "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==",
+ "dev": true
+ },
+ "array-union": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+ "dev": true
+ },
+ "array-uniq": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+ "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
+ "dev": true
+ },
+ "array-unique": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+ "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+ "dev": true
+ },
+ "arraybuffer.slice": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz",
+ "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==",
+ "dev": true
+ },
+ "arrify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
+ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=",
+ "dev": true
+ },
+ "asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=",
+ "dev": true
+ },
+ "asn1": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
+ "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "asn1.js": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
+ "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0",
+ "safer-buffer": "^2.1.0"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
+ }
+ },
+ "assert": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
+ "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4.1.1",
+ "util": "0.10.3"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+ "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
+ "dev": true
+ },
+ "util": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+ "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.1"
+ }
+ }
+ }
+ },
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
+ "dev": true
+ },
+ "assign-symbols": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+ "dev": true
+ },
+ "ast-types-flow": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
+ "integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=",
+ "dev": true
+ },
+ "async": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
+ "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.14"
+ }
+ },
+ "async-each": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
+ "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
+ "dev": true
+ },
+ "async-limiter": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
+ "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
+ "dev": true
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
+ "dev": true
+ },
+ "atob": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+ "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+ "dev": true
+ },
+ "autoprefixer": {
+ "version": "9.8.6",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.8.6.tgz",
+ "integrity": "sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.12.0",
+ "caniuse-lite": "^1.0.30001109",
+ "colorette": "^1.2.1",
+ "normalize-range": "^0.1.2",
+ "num2fraction": "^1.2.2",
+ "postcss": "^7.0.32",
+ "postcss-value-parser": "^4.1.0"
+ }
+ },
+ "aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
+ "dev": true
+ },
+ "aws4": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz",
+ "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==",
+ "dev": true
+ },
+ "axobject-query": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz",
+ "integrity": "sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww==",
+ "dev": true,
+ "requires": {
+ "ast-types-flow": "0.0.7"
+ }
+ },
+ "babel-loader": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.1.0.tgz",
+ "integrity": "sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw==",
+ "dev": true,
+ "requires": {
+ "find-cache-dir": "^2.1.0",
+ "loader-utils": "^1.4.0",
+ "mkdirp": "^0.5.3",
+ "pify": "^4.0.1",
+ "schema-utils": "^2.6.5"
+ },
+ "dependencies": {
+ "find-cache-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
+ "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
+ "dev": true,
+ "requires": {
+ "commondir": "^1.0.1",
+ "make-dir": "^2.0.0",
+ "pkg-dir": "^3.0.0"
+ }
+ },
+ "json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "loader-utils": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
+ "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
+ "dev": true,
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^1.0.1"
+ }
+ }
+ }
+ },
+ "babel-plugin-dynamic-import-node": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz",
+ "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==",
+ "dev": true,
+ "requires": {
+ "object.assign": "^4.1.0"
+ }
+ },
+ "backo2": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+ "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+ },
+ "base": {
+ "version": "0.11.2",
+ "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+ "dev": true,
+ "requires": {
+ "cache-base": "^1.0.1",
+ "class-utils": "^0.3.5",
+ "component-emitter": "^1.2.1",
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.1",
+ "mixin-deep": "^1.2.0",
+ "pascalcase": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "base64-arraybuffer": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
+ "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI=",
+ "dev": true
+ },
+ "base64-js": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
+ "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
+ "dev": true
+ },
+ "base64id": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
+ "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
+ "dev": true
+ },
+ "batch": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
+ "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=",
+ "dev": true
+ },
+ "bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+ "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
+ "dev": true,
+ "requires": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "better-assert": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
+ "integrity": "sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI=",
+ "dev": true,
+ "requires": {
+ "callsite": "1.0.0"
+ }
+ },
+ "big.js": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
+ "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
+ "dev": true
+ },
+ "binary-extensions": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
+ "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
+ "dev": true
+ },
+ "bindings": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+ "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "file-uri-to-path": "1.0.0"
+ }
+ },
+ "blob": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.5.tgz",
+ "integrity": "sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig==",
+ "dev": true
+ },
+ "blocking-proxy": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz",
+ "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "bluebird": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+ "dev": true
+ },
+ "bn.js": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz",
+ "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==",
+ "dev": true
+ },
+ "body-parser": {
+ "version": "1.19.0",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz",
+ "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==",
+ "dev": true,
+ "requires": {
+ "bytes": "3.1.0",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "http-errors": "1.7.2",
+ "iconv-lite": "0.4.24",
+ "on-finished": "~2.3.0",
+ "qs": "6.7.0",
+ "raw-body": "2.4.0",
+ "type-is": "~1.6.17"
+ },
+ "dependencies": {
+ "bytes": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+ "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
+ "dev": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "bonjour": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
+ "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=",
+ "dev": true,
+ "requires": {
+ "array-flatten": "^2.1.0",
+ "deep-equal": "^1.0.1",
+ "dns-equal": "^1.0.0",
+ "dns-txt": "^2.0.2",
+ "multicast-dns": "^6.0.1",
+ "multicast-dns-service-types": "^1.1.0"
+ }
+ },
+ "boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
+ "dev": true
+ },
+ "bootstrap": {
+ "version": "4.5.2",
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.5.2.tgz",
+ "integrity": "sha512-vlGn0bcySYl/iV+BGA544JkkZP5LB3jsmkeKLFQakCOwCM3AOk7VkldBz4jrzSe+Z0Ezn99NVXa1o45cQY4R6A=="
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "brorand": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+ "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
+ "dev": true
+ },
+ "browserify-aes": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+ "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+ "dev": true,
+ "requires": {
+ "buffer-xor": "^1.0.3",
+ "cipher-base": "^1.0.0",
+ "create-hash": "^1.1.0",
+ "evp_bytestokey": "^1.0.3",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "browserify-cipher": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
+ "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
+ "dev": true,
+ "requires": {
+ "browserify-aes": "^1.0.4",
+ "browserify-des": "^1.0.0",
+ "evp_bytestokey": "^1.0.0"
+ }
+ },
+ "browserify-des": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
+ "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.1",
+ "des.js": "^1.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "browserify-rsa": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+ "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "randombytes": "^2.0.1"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
+ }
+ },
+ "browserify-sign": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz",
+ "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^5.1.1",
+ "browserify-rsa": "^4.0.1",
+ "create-hash": "^1.2.0",
+ "create-hmac": "^1.1.7",
+ "elliptic": "^6.5.3",
+ "inherits": "^2.0.4",
+ "parse-asn1": "^5.1.5",
+ "readable-stream": "^3.6.0",
+ "safe-buffer": "^5.2.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
+ }
+ }
+ },
+ "browserify-zlib": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
+ "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
+ "dev": true,
+ "requires": {
+ "pako": "~1.0.5"
+ }
+ },
+ "browserslist": {
+ "version": "4.14.5",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.5.tgz",
+ "integrity": "sha512-Z+vsCZIvCBvqLoYkBFTwEYH3v5MCQbsAjp50ERycpOjnPmolg1Gjy4+KaWWpm8QOJt9GHkhdqAl14NpCX73CWA==",
+ "dev": true,
+ "requires": {
+ "caniuse-lite": "^1.0.30001135",
+ "electron-to-chromium": "^1.3.571",
+ "escalade": "^3.1.0",
+ "node-releases": "^1.1.61"
+ }
+ },
+ "browserstack": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.6.0.tgz",
+ "integrity": "sha512-HJDJ0TSlmkwnt9RZ+v5gFpa1XZTBYTj0ywvLwJ3241J7vMw2jAsGNVhKHtmCOyg+VxeLZyaibO9UL71AsUeDIw==",
+ "dev": true,
+ "requires": {
+ "https-proxy-agent": "^2.2.1"
+ }
+ },
+ "buffer": {
+ "version": "4.9.2",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
+ "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
+ "dev": true,
+ "requires": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4",
+ "isarray": "^1.0.0"
+ }
+ },
+ "buffer-from": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
+ "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
+ "dev": true
+ },
+ "buffer-indexof": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz",
+ "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==",
+ "dev": true
+ },
+ "buffer-xor": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+ "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
+ "dev": true
+ },
+ "builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
+ "dev": true
+ },
+ "builtin-status-codes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
+ "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
+ "dev": true
+ },
+ "builtins": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz",
+ "integrity": "sha1-y5T662HIaWRR2zZTThQi+U8K7og=",
+ "dev": true
+ },
+ "bytes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
+ "dev": true
+ },
+ "cacache": {
+ "version": "15.0.5",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.0.5.tgz",
+ "integrity": "sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A==",
+ "dev": true,
+ "requires": {
+ "@npmcli/move-file": "^1.0.1",
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.0.0",
+ "glob": "^7.1.4",
+ "infer-owner": "^1.0.4",
+ "lru-cache": "^6.0.0",
+ "minipass": "^3.1.1",
+ "minipass-collect": "^1.0.2",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.2",
+ "mkdirp": "^1.0.3",
+ "p-map": "^4.0.0",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^3.0.2",
+ "ssri": "^8.0.0",
+ "tar": "^6.0.2",
+ "unique-filename": "^1.1.1"
+ },
+ "dependencies": {
+ "mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true
+ }
+ }
+ },
+ "cache-base": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
+ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
+ "dev": true,
+ "requires": {
+ "collection-visit": "^1.0.0",
+ "component-emitter": "^1.2.1",
+ "get-value": "^2.0.6",
+ "has-value": "^1.0.0",
+ "isobject": "^3.0.1",
+ "set-value": "^2.0.0",
+ "to-object-path": "^0.3.0",
+ "union-value": "^1.0.0",
+ "unset-value": "^1.0.0"
+ }
+ },
+ "caller-callsite": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
+ "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
+ "dev": true,
+ "requires": {
+ "callsites": "^2.0.0"
+ }
+ },
+ "caller-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
+ "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
+ "dev": true,
+ "requires": {
+ "caller-callsite": "^2.0.0"
+ }
+ },
+ "callsite": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
+ "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=",
+ "dev": true
+ },
+ "callsites": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
+ "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.1.0.tgz",
+ "integrity": "sha512-WCMml9ivU60+8rEJgELlFp1gxFcEGxwYleE3bziHEDeqsqAWGHdimB7beBFGjLzVNgPGyDsfgXLQEYMpmIFnVQ==",
+ "dev": true
+ },
+ "caniuse-api": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz",
+ "integrity": "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.0.0",
+ "caniuse-lite": "^1.0.0",
+ "lodash.memoize": "^4.1.2",
+ "lodash.uniq": "^4.5.0"
+ }
+ },
+ "caniuse-lite": {
+ "version": "1.0.30001150",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001150.tgz",
+ "integrity": "sha512-kiNKvihW0m36UhAFnl7bOAv0i1K1f6wpfVtTF5O5O82XzgtBnb05V0XeV3oZ968vfg2sRNChsHw8ASH2hDfoYQ==",
+ "dev": true
+ },
+ "canonical-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-1.0.0.tgz",
+ "integrity": "sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg==",
+ "dev": true
+ },
+ "caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
+ "dev": true
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "chardet": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+ "dev": true
+ },
+ "chokidar": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz",
+ "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==",
+ "dev": true,
+ "requires": {
+ "anymatch": "~3.1.1",
+ "braces": "~3.0.2",
+ "fsevents": "~2.1.2",
+ "glob-parent": "~5.1.0",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.4.0"
+ }
+ },
+ "chownr": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+ "dev": true
+ },
+ "chrome-trace-event": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz",
+ "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
+ }
+ },
+ "cipher-base": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
+ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "circular-dependency-plugin": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz",
+ "integrity": "sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw==",
+ "dev": true
+ },
+ "class-utils": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
+ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "define-property": "^0.2.5",
+ "isobject": "^3.0.0",
+ "static-extend": "^0.1.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ }
+ }
+ },
+ "clean-stack": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+ "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+ "dev": true
+ },
+ "cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^3.1.0"
+ }
+ },
+ "cli-spinners": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.4.0.tgz",
+ "integrity": "sha512-sJAofoarcm76ZGpuooaO0eDy8saEy+YoZBLjC4h8srt4jeBnkYeOgqxgsJQTpyt2LjI5PTfLJHSL+41Yu4fEJA==",
+ "dev": true
+ },
+ "cli-width": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
+ "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
+ "dev": true
+ },
+ "cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "dev": true,
+ "requires": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "clone": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+ "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=",
+ "dev": true
+ },
+ "coa": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/coa/-/coa-2.0.2.tgz",
+ "integrity": "sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA==",
+ "dev": true,
+ "requires": {
+ "@types/q": "^1.5.1",
+ "chalk": "^2.4.1",
+ "q": "^1.1.2"
+ }
+ },
+ "codelyzer": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/codelyzer/-/codelyzer-6.0.1.tgz",
+ "integrity": "sha512-cOyGQgMdhnRYtW2xrJUNrNYDjEgwQ+BrE2y93Bwz3h4DJ6vJRLfupemU5N3pbYsUlBHJf0u1j1UGk+NLW4d97g==",
+ "dev": true,
+ "requires": {
+ "@angular/compiler": "9.0.0",
+ "@angular/core": "9.0.0",
+ "app-root-path": "^3.0.0",
+ "aria-query": "^3.0.0",
+ "axobject-query": "2.0.2",
+ "css-selector-tokenizer": "^0.7.1",
+ "cssauron": "^1.4.0",
+ "damerau-levenshtein": "^1.0.4",
+ "rxjs": "^6.5.3",
+ "semver-dsl": "^1.0.1",
+ "source-map": "^0.5.7",
+ "sprintf-js": "^1.1.2",
+ "tslib": "^1.10.0",
+ "zone.js": "~0.10.3"
+ },
+ "dependencies": {
+ "@angular/compiler": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-9.0.0.tgz",
+ "integrity": "sha512-ctjwuntPfZZT2mNj2NDIVu51t9cvbhl/16epc5xEwyzyDt76pX9UgwvY+MbXrf/C/FWwdtmNtfP698BKI+9leQ==",
+ "dev": true
+ },
+ "@angular/core": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/@angular/core/-/core-9.0.0.tgz",
+ "integrity": "sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ },
+ "sprintf-js": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
+ "integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
+ "dev": true
+ },
+ "tslib": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.0.tgz",
+ "integrity": "sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw==",
+ "dev": true
+ }
+ }
+ },
+ "collection-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
+ "dev": true,
+ "requires": {
+ "map-visit": "^1.0.0",
+ "object-visit": "^1.0.0"
+ }
+ },
+ "color": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/color/-/color-3.1.3.tgz",
+ "integrity": "sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.1",
+ "color-string": "^1.5.4"
+ }
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="
+ },
+ "color-string": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz",
+ "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==",
+ "dev": true,
+ "requires": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "colorette": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz",
+ "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==",
+ "dev": true
+ },
+ "colors": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
+ "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
+ "dev": true
+ },
+ "combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "requires": {
+ "delayed-stream": "~1.0.0"
+ }
+ },
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true
+ },
+ "commondir": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
+ "dev": true
+ },
+ "component-bind": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
+ "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=",
+ "dev": true
+ },
+ "component-emitter": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+ "dev": true
+ },
+ "component-inherit": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
+ "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=",
+ "dev": true
+ },
+ "compose-function": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/compose-function/-/compose-function-3.0.3.tgz",
+ "integrity": "sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8=",
+ "dev": true,
+ "requires": {
+ "arity-n": "^1.0.4"
+ }
+ },
+ "compressible": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+ "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+ "dev": true,
+ "requires": {
+ "mime-db": ">= 1.43.0 < 2"
+ }
+ },
+ "compression": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz",
+ "integrity": "sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.5",
+ "bytes": "3.0.0",
+ "compressible": "~2.0.16",
+ "debug": "2.6.9",
+ "on-headers": "~1.0.2",
+ "safe-buffer": "5.1.2",
+ "vary": "~1.1.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+ },
+ "concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ }
+ },
+ "connect": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
+ "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "finalhandler": "1.1.2",
+ "parseurl": "~1.3.3",
+ "utils-merge": "1.0.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "connect-history-api-fallback": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz",
+ "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==",
+ "dev": true
+ },
+ "console-browserify": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
+ "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==",
+ "dev": true
+ },
+ "constants-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
+ "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
+ "dev": true
+ },
+ "content-disposition": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
+ "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "5.1.2"
+ }
+ },
+ "content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
+ "dev": true
+ },
+ "convert-source-map": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz",
+ "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==",
+ "requires": {
+ "safe-buffer": "~5.1.1"
+ }
+ },
+ "cookie": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
+ "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==",
+ "dev": true
+ },
+ "cookie-signature": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+ "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
+ "dev": true
+ },
+ "copy-concurrently": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
+ "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
+ "dev": true,
+ "requires": {
+ "aproba": "^1.1.1",
+ "fs-write-stream-atomic": "^1.0.8",
+ "iferr": "^0.1.5",
+ "mkdirp": "^0.5.1",
+ "rimraf": "^2.5.4",
+ "run-queue": "^1.0.0"
+ },
+ "dependencies": {
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ }
+ }
+ },
+ "copy-descriptor": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
+ "dev": true
+ },
+ "copy-webpack-plugin": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-6.0.3.tgz",
+ "integrity": "sha512-q5m6Vz4elsuyVEIUXr7wJdIdePWTubsqVbEMvf1WQnHGv0Q+9yPRu7MtYFPt+GBOXRav9lvIINifTQ1vSCs+eA==",
+ "dev": true,
+ "requires": {
+ "cacache": "^15.0.4",
+ "fast-glob": "^3.2.4",
+ "find-cache-dir": "^3.3.1",
+ "glob-parent": "^5.1.1",
+ "globby": "^11.0.1",
+ "loader-utils": "^2.0.0",
+ "normalize-path": "^3.0.0",
+ "p-limit": "^3.0.1",
+ "schema-utils": "^2.7.0",
+ "serialize-javascript": "^4.0.0",
+ "webpack-sources": "^1.4.3"
+ },
+ "dependencies": {
+ "p-limit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz",
+ "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ }
+ }
+ },
+ "core-js": {
+ "version": "3.6.4",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz",
+ "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==",
+ "dev": true
+ },
+ "core-js-compat": {
+ "version": "3.6.5",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz",
+ "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.8.5",
+ "semver": "7.0.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
+ "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
+ "dev": true
+ }
+ }
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
+ "dev": true
+ },
+ "cosmiconfig": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
+ "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
+ "dev": true,
+ "requires": {
+ "import-fresh": "^2.0.0",
+ "is-directory": "^0.3.1",
+ "js-yaml": "^3.13.1",
+ "parse-json": "^4.0.0"
+ }
+ },
+ "create-ecdh": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz",
+ "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "elliptic": "^6.5.3"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
+ }
+ },
+ "create-hash": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+ "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.1",
+ "inherits": "^2.0.1",
+ "md5.js": "^1.3.4",
+ "ripemd160": "^2.0.1",
+ "sha.js": "^2.4.0"
+ }
+ },
+ "create-hmac": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+ "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+ "dev": true,
+ "requires": {
+ "cipher-base": "^1.0.3",
+ "create-hash": "^1.1.0",
+ "inherits": "^2.0.1",
+ "ripemd160": "^2.0.0",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "crypto-browserify": {
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
+ "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
+ "dev": true,
+ "requires": {
+ "browserify-cipher": "^1.0.0",
+ "browserify-sign": "^4.0.0",
+ "create-ecdh": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "create-hmac": "^1.1.0",
+ "diffie-hellman": "^5.0.0",
+ "inherits": "^2.0.1",
+ "pbkdf2": "^3.0.3",
+ "public-encrypt": "^4.0.0",
+ "randombytes": "^2.0.0",
+ "randomfill": "^1.0.3"
+ }
+ },
+ "css": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/css/-/css-2.2.4.tgz",
+ "integrity": "sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "source-map": "^0.6.1",
+ "source-map-resolve": "^0.5.2",
+ "urix": "^0.1.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "css-color-names": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
+ "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=",
+ "dev": true
+ },
+ "css-declaration-sorter": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz",
+ "integrity": "sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.1",
+ "timsort": "^0.3.0"
+ }
+ },
+ "css-loader": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-4.2.2.tgz",
+ "integrity": "sha512-omVGsTkZPVwVRpckeUnLshPp12KsmMSLqYxs12+RzM9jRR5Y+Idn/tBffjXRvOE+qW7if24cuceFJqYR5FmGBg==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^6.0.0",
+ "cssesc": "^3.0.0",
+ "icss-utils": "^4.1.1",
+ "loader-utils": "^2.0.0",
+ "postcss": "^7.0.32",
+ "postcss-modules-extract-imports": "^2.0.0",
+ "postcss-modules-local-by-default": "^3.0.3",
+ "postcss-modules-scope": "^2.2.0",
+ "postcss-modules-values": "^3.0.0",
+ "postcss-value-parser": "^4.1.0",
+ "schema-utils": "^2.7.0",
+ "semver": "^7.3.2"
+ }
+ },
+ "css-parse": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-2.0.0.tgz",
+ "integrity": "sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q=",
+ "dev": true,
+ "requires": {
+ "css": "^2.0.0"
+ }
+ },
+ "css-select": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-2.1.0.tgz",
+ "integrity": "sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ==",
+ "dev": true,
+ "requires": {
+ "boolbase": "^1.0.0",
+ "css-what": "^3.2.1",
+ "domutils": "^1.7.0",
+ "nth-check": "^1.0.2"
+ }
+ },
+ "css-select-base-adapter": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz",
+ "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==",
+ "dev": true
+ },
+ "css-selector-tokenizer": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz",
+ "integrity": "sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==",
+ "dev": true,
+ "requires": {
+ "cssesc": "^3.0.0",
+ "fastparse": "^1.1.2"
+ }
+ },
+ "css-tree": {
+ "version": "1.0.0-alpha.37",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz",
+ "integrity": "sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg==",
+ "dev": true,
+ "requires": {
+ "mdn-data": "2.0.4",
+ "source-map": "^0.6.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "css-what": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz",
+ "integrity": "sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ==",
+ "dev": true
+ },
+ "cssauron": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/cssauron/-/cssauron-1.4.0.tgz",
+ "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=",
+ "dev": true,
+ "requires": {
+ "through": "X.X.X"
+ }
+ },
+ "cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true
+ },
+ "cssnano": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-4.1.10.tgz",
+ "integrity": "sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ==",
+ "dev": true,
+ "requires": {
+ "cosmiconfig": "^5.0.0",
+ "cssnano-preset-default": "^4.0.7",
+ "is-resolvable": "^1.0.0",
+ "postcss": "^7.0.0"
+ }
+ },
+ "cssnano-preset-default": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz",
+ "integrity": "sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA==",
+ "dev": true,
+ "requires": {
+ "css-declaration-sorter": "^4.0.1",
+ "cssnano-util-raw-cache": "^4.0.1",
+ "postcss": "^7.0.0",
+ "postcss-calc": "^7.0.1",
+ "postcss-colormin": "^4.0.3",
+ "postcss-convert-values": "^4.0.1",
+ "postcss-discard-comments": "^4.0.2",
+ "postcss-discard-duplicates": "^4.0.2",
+ "postcss-discard-empty": "^4.0.1",
+ "postcss-discard-overridden": "^4.0.1",
+ "postcss-merge-longhand": "^4.0.11",
+ "postcss-merge-rules": "^4.0.3",
+ "postcss-minify-font-values": "^4.0.2",
+ "postcss-minify-gradients": "^4.0.2",
+ "postcss-minify-params": "^4.0.2",
+ "postcss-minify-selectors": "^4.0.2",
+ "postcss-normalize-charset": "^4.0.1",
+ "postcss-normalize-display-values": "^4.0.2",
+ "postcss-normalize-positions": "^4.0.2",
+ "postcss-normalize-repeat-style": "^4.0.2",
+ "postcss-normalize-string": "^4.0.2",
+ "postcss-normalize-timing-functions": "^4.0.2",
+ "postcss-normalize-unicode": "^4.0.1",
+ "postcss-normalize-url": "^4.0.1",
+ "postcss-normalize-whitespace": "^4.0.2",
+ "postcss-ordered-values": "^4.1.2",
+ "postcss-reduce-initial": "^4.0.3",
+ "postcss-reduce-transforms": "^4.0.2",
+ "postcss-svgo": "^4.0.2",
+ "postcss-unique-selectors": "^4.0.1"
+ }
+ },
+ "cssnano-util-get-arguments": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz",
+ "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=",
+ "dev": true
+ },
+ "cssnano-util-get-match": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz",
+ "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=",
+ "dev": true
+ },
+ "cssnano-util-raw-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz",
+ "integrity": "sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.0"
+ }
+ },
+ "cssnano-util-same-parent": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz",
+ "integrity": "sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q==",
+ "dev": true
+ },
+ "csso": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/csso/-/csso-4.0.3.tgz",
+ "integrity": "sha512-NL3spysxUkcrOgnpsT4Xdl2aiEiBG6bXswAABQVHcMrfjjBisFOKwLDOmf4wf32aPdcJws1zds2B0Rg+jqMyHQ==",
+ "dev": true,
+ "requires": {
+ "css-tree": "1.0.0-alpha.39"
+ },
+ "dependencies": {
+ "css-tree": {
+ "version": "1.0.0-alpha.39",
+ "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.39.tgz",
+ "integrity": "sha512-7UvkEYgBAHRG9Nt980lYxjsTrCyHFN53ky3wVsDkiMdVqylqRt+Zc+jm5qw7/qyOvN2dHSYtX0e4MbCCExSvnA==",
+ "dev": true,
+ "requires": {
+ "mdn-data": "2.0.6",
+ "source-map": "^0.6.1"
+ }
+ },
+ "mdn-data": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.6.tgz",
+ "integrity": "sha512-rQvjv71olwNHgiTbfPZFkJtjNMciWgswYeciZhtvWLO8bmX3TnhyA62I6sTWOyZssWHJJjY6/KiWwqQsWWsqOA==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "custom-event": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
+ "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=",
+ "dev": true
+ },
+ "cyclist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
+ "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
+ "dev": true
+ },
+ "d": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+ "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
+ "dev": true,
+ "requires": {
+ "es5-ext": "^0.10.50",
+ "type": "^1.0.1"
+ }
+ },
+ "damerau-levenshtein": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz",
+ "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==",
+ "dev": true
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "data-urls": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz",
+ "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==",
+ "dev": true,
+ "requires": {
+ "abab": "^2.0.3",
+ "whatwg-mimetype": "^2.3.0",
+ "whatwg-url": "^8.0.0"
+ }
+ },
+ "date-format": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/date-format/-/date-format-3.0.0.tgz",
+ "integrity": "sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w==",
+ "dev": true
+ },
+ "debug": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
+ "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
+ "requires": {
+ "ms": "2.1.2"
+ }
+ },
+ "debuglog": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz",
+ "integrity": "sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=",
+ "dev": true
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
+ },
+ "decode-uri-component": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+ "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
+ "dev": true
+ },
+ "deep-equal": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
+ "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
+ "dev": true,
+ "requires": {
+ "is-arguments": "^1.0.4",
+ "is-date-object": "^1.0.1",
+ "is-regex": "^1.0.4",
+ "object-is": "^1.0.1",
+ "object-keys": "^1.1.1",
+ "regexp.prototype.flags": "^1.2.0"
+ }
+ },
+ "default-gateway": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-4.2.0.tgz",
+ "integrity": "sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA==",
+ "dev": true,
+ "requires": {
+ "execa": "^1.0.0",
+ "ip-regex": "^2.1.0"
+ }
+ },
+ "defaults": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
+ "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=",
+ "dev": true,
+ "requires": {
+ "clone": "^1.0.2"
+ },
+ "dependencies": {
+ "clone": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+ "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
+ "dev": true
+ }
+ }
+ },
+ "define-properties": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+ "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+ "dev": true,
+ "requires": {
+ "object-keys": "^1.0.12"
+ }
+ },
+ "define-property": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
+ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.2",
+ "isobject": "^3.0.1"
+ },
+ "dependencies": {
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "del": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz",
+ "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==",
+ "dev": true,
+ "requires": {
+ "@types/glob": "^7.1.1",
+ "globby": "^6.1.0",
+ "is-path-cwd": "^2.0.0",
+ "is-path-in-cwd": "^2.0.0",
+ "p-map": "^2.0.0",
+ "pify": "^4.0.1",
+ "rimraf": "^2.6.3"
+ },
+ "dependencies": {
+ "array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+ "dev": true,
+ "requires": {
+ "array-uniq": "^1.0.1"
+ }
+ },
+ "globby": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+ "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
+ "dev": true,
+ "requires": {
+ "array-union": "^1.0.1",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ }
+ }
+ },
+ "p-map": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz",
+ "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ }
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
+ "dev": true
+ },
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
+ "dev": true
+ },
+ "dependency-graph": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.7.2.tgz",
+ "integrity": "sha512-KqtH4/EZdtdfWX0p6MGP9jljvxSY6msy/pRUD4jgNwVpv3v1QmNLlsB3LDSSUg79BRVSn7jI1QPRtArGABovAQ==",
+ "dev": true
+ },
+ "des.js": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
+ "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "destroy": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+ "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
+ "dev": true
+ },
+ "detect-node": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz",
+ "integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==",
+ "dev": true
+ },
+ "dezalgo": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.3.tgz",
+ "integrity": "sha1-f3Qt4Gb8dIvI24IFad3c5Jvw1FY=",
+ "dev": true,
+ "requires": {
+ "asap": "^2.0.0",
+ "wrappy": "1"
+ }
+ },
+ "di": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz",
+ "integrity": "sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=",
+ "dev": true
+ },
+ "diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true
+ },
+ "diffie-hellman": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
+ "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "miller-rabin": "^4.0.0",
+ "randombytes": "^2.0.0"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
+ }
+ },
+ "dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+ "dev": true,
+ "requires": {
+ "path-type": "^4.0.0"
+ }
+ },
+ "dns-equal": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
+ "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=",
+ "dev": true
+ },
+ "dns-packet": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-1.3.1.tgz",
+ "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==",
+ "dev": true,
+ "requires": {
+ "ip": "^1.1.0",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "dns-txt": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz",
+ "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=",
+ "dev": true,
+ "requires": {
+ "buffer-indexof": "^1.0.0"
+ }
+ },
+ "dom-serialize": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz",
+ "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=",
+ "dev": true,
+ "requires": {
+ "custom-event": "~1.0.0",
+ "ent": "~2.2.0",
+ "extend": "^3.0.0",
+ "void-elements": "^2.0.0"
+ }
+ },
+ "dom-serializer": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
+ "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^2.0.1",
+ "entities": "^2.0.0"
+ },
+ "dependencies": {
+ "domelementtype": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.2.tgz",
+ "integrity": "sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA==",
+ "dev": true
+ }
+ }
+ },
+ "domain-browser": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
+ "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
+ "dev": true
+ },
+ "domelementtype": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
+ "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==",
+ "dev": true
+ },
+ "domutils": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz",
+ "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==",
+ "dev": true,
+ "requires": {
+ "dom-serializer": "0",
+ "domelementtype": "1"
+ }
+ },
+ "dot-prop": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
+ "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
+ "dev": true,
+ "requires": {
+ "is-obj": "^2.0.0"
+ }
+ },
+ "duplexify": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
+ "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.0.0",
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.0",
+ "stream-shift": "^1.0.0"
+ }
+ },
+ "ecc-jsbn": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+ "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
+ "dev": true,
+ "requires": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
+ "dev": true
+ },
+ "electron-to-chromium": {
+ "version": "1.3.583",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.583.tgz",
+ "integrity": "sha512-L9BwLwJohjZW9mQESI79HRzhicPk1DFgM+8hOCfGgGCFEcA3Otpv7QK6SGtYoZvfQfE3wKLh0Hd5ptqUFv3gvQ==",
+ "dev": true
+ },
+ "elliptic": {
+ "version": "6.5.3",
+ "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
+ "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.4.0",
+ "brorand": "^1.0.1",
+ "hash.js": "^1.0.0",
+ "hmac-drbg": "^1.0.0",
+ "inherits": "^2.0.1",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.0"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
+ }
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "emojis-list": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
+ "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
+ "dev": true
+ },
+ "encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
+ "dev": true
+ },
+ "encoding": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
+ "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
+ "dev": true,
+ "requires": {
+ "iconv-lite": "^0.6.2"
+ }
+ },
+ "end-of-stream": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+ "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+ "dev": true,
+ "requires": {
+ "once": "^1.4.0"
+ }
+ },
+ "engine.io": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.2.tgz",
+ "integrity": "sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.4",
+ "base64id": "2.0.0",
+ "cookie": "0.3.1",
+ "debug": "~4.1.0",
+ "engine.io-parser": "~2.2.0",
+ "ws": "^7.1.2"
+ },
+ "dependencies": {
+ "cookie": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
+ "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=",
+ "dev": true
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "ws": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
+ "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==",
+ "dev": true
+ }
+ }
+ },
+ "engine.io-client": {
+ "version": "3.4.4",
+ "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.4.tgz",
+ "integrity": "sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ==",
+ "dev": true,
+ "requires": {
+ "component-emitter": "~1.3.0",
+ "component-inherit": "0.0.3",
+ "debug": "~3.1.0",
+ "engine.io-parser": "~2.2.0",
+ "has-cors": "1.1.0",
+ "indexof": "0.0.1",
+ "parseqs": "0.0.6",
+ "parseuri": "0.0.6",
+ "ws": "~6.1.0",
+ "xmlhttprequest-ssl": "~1.5.4",
+ "yeast": "0.1.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "parseqs": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
+ "integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w==",
+ "dev": true
+ },
+ "parseuri": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
+ "integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow==",
+ "dev": true
+ },
+ "ws": {
+ "version": "6.1.4",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
+ "integrity": "sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA==",
+ "dev": true,
+ "requires": {
+ "async-limiter": "~1.0.0"
+ }
+ }
+ }
+ },
+ "engine.io-parser": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz",
+ "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==",
+ "dev": true,
+ "requires": {
+ "after": "0.8.2",
+ "arraybuffer.slice": "~0.0.7",
+ "base64-arraybuffer": "0.1.4",
+ "blob": "0.0.5",
+ "has-binary2": "~1.0.2"
+ }
+ },
+ "enhanced-resolve": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.3.0.tgz",
+ "integrity": "sha512-3e87LvavsdxyoCfGusJnrZ5G8SLPOFeHSNpZI/ATL9a5leXo2k0w6MKnbqhdBad9qTobSfB20Ld7UmgoNbAZkQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "memory-fs": "^0.5.0",
+ "tapable": "^1.0.0"
+ }
+ },
+ "ent": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.0.tgz",
+ "integrity": "sha1-6WQhkyWiHQX0RGai9obtbOX13R0=",
+ "dev": true
+ },
+ "entities": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz",
+ "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==",
+ "dev": true
+ },
+ "err-code": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/err-code/-/err-code-1.1.2.tgz",
+ "integrity": "sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA=",
+ "dev": true
+ },
+ "errno": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
+ "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
+ "dev": true,
+ "requires": {
+ "prr": "~1.0.1"
+ }
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "es-abstract": {
+ "version": "1.18.0-next.1",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
+ "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-negative-zero": "^2.0.0",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "dev": true,
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "es5-ext": {
+ "version": "0.10.53",
+ "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
+ "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
+ "dev": true,
+ "requires": {
+ "es6-iterator": "~2.0.3",
+ "es6-symbol": "~3.1.3",
+ "next-tick": "~1.0.0"
+ }
+ },
+ "es6-iterator": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+ "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+ "dev": true,
+ "requires": {
+ "d": "1",
+ "es5-ext": "^0.10.35",
+ "es6-symbol": "^3.1.1"
+ }
+ },
+ "es6-promise": {
+ "version": "4.2.8",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
+ "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
+ "dev": true
+ },
+ "es6-promisify": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
+ "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
+ "dev": true,
+ "requires": {
+ "es6-promise": "^4.0.3"
+ }
+ },
+ "es6-symbol": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
+ "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+ "dev": true,
+ "requires": {
+ "d": "^1.0.1",
+ "ext": "^1.1.2"
+ }
+ },
+ "escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true
+ },
+ "escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
+ },
+ "eslint-scope": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
+ "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.1.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^5.2.0"
+ },
+ "dependencies": {
+ "estraverse": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+ "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+ "dev": true
+ }
+ }
+ },
+ "estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true
+ },
+ "etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
+ "dev": true
+ },
+ "eventemitter3": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+ "dev": true
+ },
+ "events": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz",
+ "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==",
+ "dev": true
+ },
+ "eventsource": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz",
+ "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==",
+ "dev": true,
+ "requires": {
+ "original": "^1.0.0"
+ }
+ },
+ "evp_bytestokey": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+ "dev": true,
+ "requires": {
+ "md5.js": "^1.3.4",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "execa": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+ "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
+ "dev": true,
+ "requires": {
+ "cross-spawn": "^6.0.0",
+ "get-stream": "^4.0.0",
+ "is-stream": "^1.1.0",
+ "npm-run-path": "^2.0.0",
+ "p-finally": "^1.0.0",
+ "signal-exit": "^3.0.0",
+ "strip-eof": "^1.0.0"
+ }
+ },
+ "exit": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+ "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
+ "dev": true
+ },
+ "expand-brackets": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+ "dev": true,
+ "requires": {
+ "debug": "^2.3.3",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "posix-character-classes": "^0.1.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "express": {
+ "version": "4.17.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz",
+ "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.7",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.19.0",
+ "content-disposition": "0.5.3",
+ "content-type": "~1.0.4",
+ "cookie": "0.4.0",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "~1.1.2",
+ "fresh": "0.5.2",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.5",
+ "qs": "6.7.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.1.2",
+ "send": "0.17.1",
+ "serve-static": "1.14.1",
+ "setprototypeof": "1.1.1",
+ "statuses": "~1.5.0",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "dependencies": {
+ "array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
+ "dev": true
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "ext": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
+ "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
+ "dev": true,
+ "requires": {
+ "type": "^2.0.0"
+ },
+ "dependencies": {
+ "type": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz",
+ "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==",
+ "dev": true
+ }
+ }
+ },
+ "extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true
+ },
+ "extend-shallow": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+ "dev": true,
+ "requires": {
+ "assign-symbols": "^1.0.0",
+ "is-extendable": "^1.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "external-editor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+ "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+ "dev": true,
+ "requires": {
+ "chardet": "^0.7.0",
+ "iconv-lite": "^0.4.24",
+ "tmp": "^0.0.33"
+ },
+ "dependencies": {
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ }
+ }
+ },
+ "extglob": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+ "dev": true,
+ "requires": {
+ "array-unique": "^0.3.2",
+ "define-property": "^1.0.0",
+ "expand-brackets": "^2.1.4",
+ "extend-shallow": "^2.0.1",
+ "fragment-cache": "^0.2.1",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+ "dev": true
+ },
+ "fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true
+ },
+ "fast-glob": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz",
+ "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==",
+ "dev": true,
+ "requires": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.0",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.2",
+ "picomatch": "^2.2.1"
+ }
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true
+ },
+ "fastparse": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
+ "integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==",
+ "dev": true
+ },
+ "fastq": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz",
+ "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==",
+ "dev": true,
+ "requires": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "faye-websocket": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
+ "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=",
+ "dev": true,
+ "requires": {
+ "websocket-driver": ">=0.5.1"
+ }
+ },
+ "figgy-pudding": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
+ "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==",
+ "dev": true
+ },
+ "figures": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
+ "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5"
+ }
+ },
+ "file-loader": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.0.0.tgz",
+ "integrity": "sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^2.0.0",
+ "schema-utils": "^2.6.5"
+ }
+ },
+ "file-uri-to-path": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+ "dev": true,
+ "optional": true
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "finalhandler": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+ "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "statuses": "~1.5.0",
+ "unpipe": "~1.0.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "find-cache-dir": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.1.tgz",
+ "integrity": "sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ==",
+ "dev": true,
+ "requires": {
+ "commondir": "^1.0.1",
+ "make-dir": "^3.0.2",
+ "pkg-dir": "^4.1.0"
+ },
+ "dependencies": {
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
+ },
+ "make-dir": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "dev": true,
+ "requires": {
+ "semver": "^6.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "pkg-dir": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
+ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
+ "dev": true,
+ "requires": {
+ "find-up": "^4.0.0"
+ }
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^3.0.0"
+ }
+ },
+ "flatted": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
+ "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
+ "dev": true
+ },
+ "flush-write-stream": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
+ "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.3.6"
+ }
+ },
+ "follow-redirects": {
+ "version": "1.13.0",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz",
+ "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==",
+ "dev": true
+ },
+ "for-in": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+ "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+ "dev": true
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+ "dev": true
+ },
+ "form-data": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+ "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+ "dev": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ }
+ },
+ "forwarded": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+ "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
+ "dev": true
+ },
+ "fragment-cache": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+ "dev": true,
+ "requires": {
+ "map-cache": "^0.2.2"
+ }
+ },
+ "fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
+ "dev": true
+ },
+ "from2": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
+ "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.0.0"
+ }
+ },
+ "fs-extra": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.2.tgz",
+ "integrity": "sha1-+RcExT0bRh+JNFKwwwfZmXZHq2s=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "fs-minipass": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ }
+ },
+ "fs-write-stream-atomic": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
+ "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.2",
+ "iferr": "^0.1.5",
+ "imurmurhash": "^0.1.4",
+ "readable-stream": "1 || 2"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+ },
+ "fsevents": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+ "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+ "dev": true,
+ "optional": true
+ },
+ "function-bind": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+ "dev": true
+ },
+ "genfun": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/genfun/-/genfun-5.0.0.tgz",
+ "integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==",
+ "dev": true
+ },
+ "gensync": {
+ "version": "1.0.0-beta.1",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz",
+ "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg=="
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
+ },
+ "get-stream": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
+ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
+ "dev": true,
+ "requires": {
+ "pump": "^3.0.0"
+ }
+ },
+ "get-value": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+ "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
+ "dev": true
+ },
+ "getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.6",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+ "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+ "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "globals": {
+ "version": "11.12.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
+ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
+ },
+ "globby": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz",
+ "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==",
+ "dev": true,
+ "requires": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.1.1",
+ "ignore": "^5.1.4",
+ "merge2": "^1.3.0",
+ "slash": "^3.0.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+ "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+ "dev": true
+ },
+ "handle-thing": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz",
+ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==",
+ "dev": true
+ },
+ "har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
+ "dev": true
+ },
+ "har-validator": {
+ "version": "5.1.5",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+ "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.12.3",
+ "har-schema": "^2.0.0"
+ }
+ },
+ "has": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+ "dev": true,
+ "requires": {
+ "function-bind": "^1.1.1"
+ }
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "has-binary2": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz",
+ "integrity": "sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw==",
+ "dev": true,
+ "requires": {
+ "isarray": "2.0.1"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
+ "dev": true
+ }
+ }
+ },
+ "has-cors": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-cors/-/has-cors-1.1.0.tgz",
+ "integrity": "sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk=",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
+ },
+ "has-symbols": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
+ "dev": true
+ },
+ "has-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.6",
+ "has-values": "^1.0.0",
+ "isobject": "^3.0.0"
+ }
+ },
+ "has-values": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "kind-of": "^4.0.0"
+ },
+ "dependencies": {
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "kind-of": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "hash-base": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
+ "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.6.0",
+ "safe-buffer": "^5.2.0"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
+ }
+ }
+ },
+ "hash.js": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+ "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "minimalistic-assert": "^1.0.1"
+ }
+ },
+ "hex-color-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
+ "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ==",
+ "dev": true
+ },
+ "hmac-drbg": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+ "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
+ "dev": true,
+ "requires": {
+ "hash.js": "^1.0.3",
+ "minimalistic-assert": "^1.0.0",
+ "minimalistic-crypto-utils": "^1.0.1"
+ }
+ },
+ "hosted-git-info": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-3.0.5.tgz",
+ "integrity": "sha512-i4dpK6xj9BIpVOTboXIlKG9+8HMKggcrMX7WA24xZtKwX0TPelq/rbaS5rCKeNX8sJXZJGdSxpnEGtta+wismQ==",
+ "dev": true,
+ "requires": {
+ "lru-cache": "^6.0.0"
+ }
+ },
+ "hpack.js": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
+ "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "obuf": "^1.0.0",
+ "readable-stream": "^2.0.1",
+ "wbuf": "^1.1.0"
+ }
+ },
+ "hsl-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz",
+ "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=",
+ "dev": true
+ },
+ "hsla-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz",
+ "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=",
+ "dev": true
+ },
+ "html-comment-regex": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz",
+ "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==",
+ "dev": true
+ },
+ "html-entities": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz",
+ "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==",
+ "dev": true
+ },
+ "html-escaper": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+ "dev": true
+ },
+ "http-cache-semantics": {
+ "version": "3.8.1",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz",
+ "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==",
+ "dev": true
+ },
+ "http-deceiver": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
+ "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=",
+ "dev": true
+ },
+ "http-errors": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
+ "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==",
+ "dev": true,
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.1",
+ "statuses": ">= 1.5.0 < 2",
+ "toidentifier": "1.0.0"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+ "dev": true
+ }
+ }
+ },
+ "http-proxy": {
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+ "dev": true,
+ "requires": {
+ "eventemitter3": "^4.0.0",
+ "follow-redirects": "^1.0.0",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "http-proxy-agent": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz",
+ "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==",
+ "dev": true,
+ "requires": {
+ "agent-base": "4",
+ "debug": "3.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "http-proxy-middleware": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz",
+ "integrity": "sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q==",
+ "dev": true,
+ "requires": {
+ "http-proxy": "^1.17.0",
+ "is-glob": "^4.0.0",
+ "lodash": "^4.17.11",
+ "micromatch": "^3.1.10"
+ },
+ "dependencies": {
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ }
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ }
+ }
+ },
+ "http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^1.2.2",
+ "sshpk": "^1.7.0"
+ }
+ },
+ "https-browserify": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
+ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
+ "dev": true
+ },
+ "https-proxy-agent": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
+ "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
+ "dev": true,
+ "requires": {
+ "agent-base": "^4.3.0",
+ "debug": "^3.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ },
+ "humanize-ms": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
+ "integrity": "sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0=",
+ "dev": true,
+ "requires": {
+ "ms": "^2.0.0"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
+ "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ }
+ },
+ "icss-utils": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz",
+ "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.14"
+ }
+ },
+ "ieee754": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+ "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
+ "dev": true
+ },
+ "iferr": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
+ "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
+ "dev": true
+ },
+ "ignore": {
+ "version": "5.1.8",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
+ "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==",
+ "dev": true
+ },
+ "ignore-walk": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
+ "integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
+ "dev": true,
+ "requires": {
+ "minimatch": "^3.0.4"
+ }
+ },
+ "image-size": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
+ "integrity": "sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w=",
+ "dev": true,
+ "optional": true
+ },
+ "immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=",
+ "dev": true
+ },
+ "import-cwd": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
+ "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=",
+ "dev": true,
+ "requires": {
+ "import-from": "^2.1.0"
+ }
+ },
+ "import-fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
+ "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
+ "dev": true,
+ "requires": {
+ "caller-path": "^2.0.0",
+ "resolve-from": "^3.0.0"
+ }
+ },
+ "import-from": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz",
+ "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=",
+ "dev": true,
+ "requires": {
+ "resolve-from": "^3.0.0"
+ }
+ },
+ "import-local": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
+ "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==",
+ "dev": true,
+ "requires": {
+ "pkg-dir": "^3.0.0",
+ "resolve-cwd": "^2.0.0"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "dev": true
+ },
+ "indent-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+ "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+ "dev": true
+ },
+ "indexes-of": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
+ "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
+ "dev": true
+ },
+ "indexof": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
+ "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=",
+ "dev": true
+ },
+ "infer-owner": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
+ "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
+ },
+ "ini": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
+ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
+ "dev": true
+ },
+ "inquirer": {
+ "version": "7.3.3",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.3.3.tgz",
+ "integrity": "sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.1.0",
+ "cli-cursor": "^3.1.0",
+ "cli-width": "^3.0.0",
+ "external-editor": "^3.0.3",
+ "figures": "^3.0.0",
+ "lodash": "^4.17.19",
+ "mute-stream": "0.0.8",
+ "run-async": "^2.4.0",
+ "rxjs": "^6.6.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "through": "^2.3.6"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+ "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+ "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "internal-ip": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/internal-ip/-/internal-ip-4.3.0.tgz",
+ "integrity": "sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg==",
+ "dev": true,
+ "requires": {
+ "default-gateway": "^4.2.0",
+ "ipaddr.js": "^1.9.0"
+ }
+ },
+ "invariant": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
+ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
+ "dev": true,
+ "requires": {
+ "loose-envify": "^1.0.0"
+ }
+ },
+ "ip": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
+ "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
+ "dev": true
+ },
+ "ip-regex": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
+ "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
+ "dev": true
+ },
+ "ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "dev": true
+ },
+ "is-absolute-url": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz",
+ "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=",
+ "dev": true
+ },
+ "is-accessor-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-arguments": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz",
+ "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==",
+ "dev": true
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+ "dev": true
+ },
+ "is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-buffer": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
+ "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
+ "dev": true
+ },
+ "is-callable": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
+ "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==",
+ "dev": true
+ },
+ "is-color-stop": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz",
+ "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=",
+ "dev": true,
+ "requires": {
+ "css-color-names": "^0.0.4",
+ "hex-color-regex": "^1.1.0",
+ "hsl-regex": "^1.0.0",
+ "hsla-regex": "^1.0.0",
+ "rgb-regex": "^1.0.1",
+ "rgba-regex": "^1.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-date-object": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+ "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
+ "dev": true
+ },
+ "is-descriptor": {
+ "version": "0.1.6",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
+ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^0.1.6",
+ "is-data-descriptor": "^0.1.4",
+ "kind-of": "^5.0.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
+ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
+ "dev": true
+ }
+ }
+ },
+ "is-directory": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
+ "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
+ "dev": true
+ },
+ "is-docker": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz",
+ "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==",
+ "dev": true
+ },
+ "is-extendable": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+ "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
+ "dev": true
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-interactive": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
+ "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+ "dev": true
+ },
+ "is-negative-zero": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
+ "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=",
+ "dev": true
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
+ "is-obj": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
+ "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+ "dev": true
+ },
+ "is-path-cwd": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
+ "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==",
+ "dev": true
+ },
+ "is-path-in-cwd": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz",
+ "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==",
+ "dev": true,
+ "requires": {
+ "is-path-inside": "^2.1.0"
+ }
+ },
+ "is-path-inside": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz",
+ "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==",
+ "dev": true,
+ "requires": {
+ "path-is-inside": "^1.0.2"
+ }
+ },
+ "is-plain-obj": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
+ "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
+ "dev": true
+ },
+ "is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "is-regex": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
+ "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
+ "dev": true,
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "is-resolvable": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz",
+ "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==",
+ "dev": true
+ },
+ "is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
+ "dev": true
+ },
+ "is-svg": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz",
+ "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==",
+ "dev": true,
+ "requires": {
+ "html-comment-regex": "^1.1.0"
+ }
+ },
+ "is-symbol": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+ "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
+ "dev": true,
+ "requires": {
+ "has-symbols": "^1.0.1"
+ }
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
+ "dev": true
+ },
+ "is-windows": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
+ "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
+ "dev": true
+ },
+ "is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dev": true,
+ "requires": {
+ "is-docker": "^2.0.0"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
+ "dev": true
+ },
+ "isbinaryfile": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.6.tgz",
+ "integrity": "sha512-ORrEy+SNVqUhrCaal4hA4fBzhggQQ+BaLntyPOdoEiwlKZW9BZiJXjg3RMiruE4tPEI3pyVPpySHQF/dKWperg==",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
+ "dev": true
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
+ "dev": true
+ },
+ "istanbul-lib-coverage": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz",
+ "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==",
+ "dev": true
+ },
+ "istanbul-lib-instrument": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz",
+ "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==",
+ "dev": true,
+ "requires": {
+ "@babel/core": "^7.7.5",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-coverage": "^3.0.0",
+ "semver": "^6.3.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "istanbul-lib-report": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
+ "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
+ "dev": true,
+ "requires": {
+ "istanbul-lib-coverage": "^3.0.0",
+ "make-dir": "^3.0.0",
+ "supports-color": "^7.1.0"
+ },
+ "dependencies": {
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "make-dir": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
+ "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
+ "dev": true,
+ "requires": {
+ "semver": "^6.0.0"
+ }
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "istanbul-lib-source-maps": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz",
+ "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^2.0.5",
+ "make-dir": "^2.1.0",
+ "rimraf": "^2.6.3",
+ "source-map": "^0.6.1"
+ },
+ "dependencies": {
+ "istanbul-lib-coverage": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz",
+ "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "istanbul-reports": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz",
+ "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==",
+ "dev": true,
+ "requires": {
+ "html-escaper": "^2.0.0",
+ "istanbul-lib-report": "^3.0.0"
+ }
+ },
+ "jasmine": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz",
+ "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=",
+ "dev": true,
+ "requires": {
+ "exit": "^0.1.2",
+ "glob": "^7.0.6",
+ "jasmine-core": "~2.8.0"
+ },
+ "dependencies": {
+ "jasmine-core": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz",
+ "integrity": "sha1-vMl5rh+f0FcB5F5S5l06XWPxok4=",
+ "dev": true
+ }
+ }
+ },
+ "jasmine-core": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.6.0.tgz",
+ "integrity": "sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==",
+ "dev": true
+ },
+ "jasmine-spec-reporter": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/jasmine-spec-reporter/-/jasmine-spec-reporter-5.0.2.tgz",
+ "integrity": "sha512-6gP1LbVgJ+d7PKksQBc2H0oDGNRQI3gKUsWlswKaQ2fif9X5gzhQcgM5+kiJGCQVurOG09jqNhk7payggyp5+g==",
+ "dev": true,
+ "requires": {
+ "colors": "1.4.0"
+ }
+ },
+ "jasminewd2": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz",
+ "integrity": "sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4=",
+ "dev": true
+ },
+ "jest-worker": {
+ "version": "26.3.0",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.3.0.tgz",
+ "integrity": "sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^7.0.0"
+ },
+ "dependencies": {
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+ },
+ "js-yaml": {
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz",
+ "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+ "dev": true
+ },
+ "jsesc": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
+ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
+ },
+ "json-parse-better-errors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+ "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
+ "dev": true
+ },
+ "json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+ "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+ "dev": true
+ },
+ "json-schema": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
+ "dev": true
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
+ "dev": true
+ },
+ "json3": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.3.tgz",
+ "integrity": "sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA==",
+ "dev": true
+ },
+ "json5": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
+ "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "jsonc-parser": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.0.tgz",
+ "integrity": "sha512-b0EBt8SWFNnixVdvoR2ZtEGa9ZqLhbJnOjezn+WP+8kspFm+PFYDN8Z4Bc7pRlDjvuVcADSUkroIuTWWn/YiIA==",
+ "dev": true
+ },
+ "jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "jsonparse": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=",
+ "dev": true
+ },
+ "jsprim": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+ "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.2.3",
+ "verror": "1.10.0"
+ }
+ },
+ "jszip": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz",
+ "integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==",
+ "dev": true,
+ "requires": {
+ "lie": "~3.3.0",
+ "pako": "~1.0.2",
+ "readable-stream": "~2.3.6",
+ "set-immediate-shim": "~1.0.1"
+ }
+ },
+ "karma": {
+ "version": "5.0.9",
+ "resolved": "https://registry.npmjs.org/karma/-/karma-5.0.9.tgz",
+ "integrity": "sha512-dUA5z7Lo7G4FRSe1ZAXqOINEEWxmCjDBbfRBmU/wYlSMwxUQJP/tEEP90yJt3Uqo03s9rCgVnxtlfq+uDhxSPg==",
+ "dev": true,
+ "requires": {
+ "body-parser": "^1.19.0",
+ "braces": "^3.0.2",
+ "chokidar": "^3.0.0",
+ "colors": "^1.4.0",
+ "connect": "^3.7.0",
+ "di": "^0.0.1",
+ "dom-serialize": "^2.2.1",
+ "flatted": "^2.0.2",
+ "glob": "^7.1.6",
+ "graceful-fs": "^4.2.4",
+ "http-proxy": "^1.18.1",
+ "isbinaryfile": "^4.0.6",
+ "lodash": "^4.17.15",
+ "log4js": "^6.2.1",
+ "mime": "^2.4.5",
+ "minimatch": "^3.0.4",
+ "qjobs": "^1.2.0",
+ "range-parser": "^1.2.1",
+ "rimraf": "^3.0.2",
+ "socket.io": "^2.3.0",
+ "source-map": "^0.6.1",
+ "tmp": "0.2.1",
+ "ua-parser-js": "0.7.21",
+ "yargs": "^15.3.1"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
+ },
+ "mime": {
+ "version": "2.4.6",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
+ "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==",
+ "dev": true
+ },
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+ "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ },
+ "tmp": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
+ "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
+ "dev": true,
+ "requires": {
+ "rimraf": "^3.0.0"
+ }
+ },
+ "wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "yargs": {
+ "version": "15.4.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "dev": true,
+ "requires": {
+ "cliui": "^6.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^4.1.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^4.2.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^18.1.2"
+ }
+ },
+ "yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
+ "karma-chrome-launcher": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz",
+ "integrity": "sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg==",
+ "dev": true,
+ "requires": {
+ "which": "^1.2.1"
+ }
+ },
+ "karma-coverage-istanbul-reporter": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz",
+ "integrity": "sha512-wE4VFhG/QZv2Y4CdAYWDbMmcAHeS926ZIji4z+FkB2aF/EposRb6DP6G5ncT/wXhqUfAb/d7kZrNKPonbvsATw==",
+ "dev": true,
+ "requires": {
+ "istanbul-lib-coverage": "^3.0.0",
+ "istanbul-lib-report": "^3.0.0",
+ "istanbul-lib-source-maps": "^3.0.6",
+ "istanbul-reports": "^3.0.2",
+ "minimatch": "^3.0.4"
+ }
+ },
+ "karma-jasmine": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-4.0.1.tgz",
+ "integrity": "sha512-h8XDAhTiZjJKzfkoO1laMH+zfNlra+dEQHUAjpn5JV1zCPtOIVWGQjLBrqhnzQa/hrU2XrZwSyBa6XjEBzfXzw==",
+ "dev": true,
+ "requires": {
+ "jasmine-core": "^3.6.0"
+ }
+ },
+ "karma-jasmine-html-reporter": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.4.tgz",
+ "integrity": "sha512-PtilRLno5O6wH3lDihRnz0Ba8oSn0YUJqKjjux1peoYGwo0AQqrWRbdWk/RLzcGlb+onTyXAnHl6M+Hu3UxG/Q==",
+ "dev": true
+ },
+ "karma-source-map-support": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz",
+ "integrity": "sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==",
+ "dev": true,
+ "requires": {
+ "source-map-support": "^0.5.5"
+ }
+ },
+ "killable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
+ "integrity": "sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg==",
+ "dev": true
+ },
+ "kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true
+ },
+ "klona": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.4.tgz",
+ "integrity": "sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA==",
+ "dev": true
+ },
+ "less": {
+ "version": "3.12.2",
+ "resolved": "https://registry.npmjs.org/less/-/less-3.12.2.tgz",
+ "integrity": "sha512-+1V2PCMFkL+OIj2/HrtrvZw0BC0sYLMICJfbQjuj/K8CEnlrFX6R5cKKgzzttsZDHyxQNL1jqMREjKN3ja/E3Q==",
+ "dev": true,
+ "requires": {
+ "errno": "^0.1.1",
+ "graceful-fs": "^4.1.2",
+ "image-size": "~0.5.0",
+ "make-dir": "^2.1.0",
+ "mime": "^1.4.1",
+ "native-request": "^1.0.5",
+ "source-map": "~0.6.0",
+ "tslib": "^1.10.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "optional": true
+ },
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "dev": true
+ }
+ }
+ },
+ "less-loader": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-6.2.0.tgz",
+ "integrity": "sha512-Cl5h95/Pz/PWub/tCBgT1oNMFeH1WTD33piG80jn5jr12T4XbxZcjThwNXDQ7AG649WEynuIzO4b0+2Tn9Qolg==",
+ "dev": true,
+ "requires": {
+ "clone": "^2.1.2",
+ "less": "^3.11.3",
+ "loader-utils": "^2.0.0",
+ "schema-utils": "^2.7.0"
+ }
+ },
+ "leven": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
+ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
+ "dev": true
+ },
+ "levenary": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz",
+ "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==",
+ "dev": true,
+ "requires": {
+ "leven": "^3.1.0"
+ }
+ },
+ "license-webpack-plugin": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-2.3.0.tgz",
+ "integrity": "sha512-JK/DXrtN6UeYQSgkg5q1+pgJ8aiKPL9tnz9Wzw+Ikkf+8mJxG56x6t8O+OH/tAeF/5NREnelTEMyFtbJNkjH4w==",
+ "dev": true,
+ "requires": {
+ "@types/webpack-sources": "^0.1.5",
+ "webpack-sources": "^1.2.0"
+ }
+ },
+ "lie": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+ "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+ "dev": true,
+ "requires": {
+ "immediate": "~3.0.5"
+ }
+ },
+ "loader-runner": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
+ "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==",
+ "dev": true
+ },
+ "loader-utils": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
+ "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
+ "dev": true,
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^2.1.2"
+ }
+ },
+ "locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.20",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
+ "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
+ },
+ "lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=",
+ "dev": true
+ },
+ "lodash.memoize": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+ "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
+ "dev": true
+ },
+ "lodash.sortby": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+ "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=",
+ "dev": true
+ },
+ "lodash.uniq": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+ "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
+ "dev": true
+ },
+ "log-symbols": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz",
+ "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==",
+ "dev": true,
+ "requires": {
+ "chalk": "^4.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+ "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "log4js": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.3.0.tgz",
+ "integrity": "sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw==",
+ "dev": true,
+ "requires": {
+ "date-format": "^3.0.0",
+ "debug": "^4.1.1",
+ "flatted": "^2.0.1",
+ "rfdc": "^1.1.4",
+ "streamroller": "^2.2.4"
+ }
+ },
+ "loglevel": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.7.0.tgz",
+ "integrity": "sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ==",
+ "dev": true
+ },
+ "loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "dev": true,
+ "requires": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ }
+ },
+ "lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "magic-string": {
+ "version": "0.25.7",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
+ "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
+ "dev": true,
+ "requires": {
+ "sourcemap-codec": "^1.4.4"
+ }
+ },
+ "make-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
+ "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
+ "dev": true,
+ "requires": {
+ "pify": "^4.0.1",
+ "semver": "^5.6.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "dev": true
+ },
+ "make-fetch-happen": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz",
+ "integrity": "sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag==",
+ "dev": true,
+ "requires": {
+ "agentkeepalive": "^3.4.1",
+ "cacache": "^12.0.0",
+ "http-cache-semantics": "^3.8.1",
+ "http-proxy-agent": "^2.1.0",
+ "https-proxy-agent": "^2.2.3",
+ "lru-cache": "^5.1.1",
+ "mississippi": "^3.0.0",
+ "node-fetch-npm": "^2.0.2",
+ "promise-retry": "^1.1.1",
+ "socks-proxy-agent": "^4.0.0",
+ "ssri": "^6.0.0"
+ },
+ "dependencies": {
+ "cacache": {
+ "version": "12.0.4",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
+ "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.5.5",
+ "chownr": "^1.1.1",
+ "figgy-pudding": "^3.5.1",
+ "glob": "^7.1.4",
+ "graceful-fs": "^4.1.15",
+ "infer-owner": "^1.0.3",
+ "lru-cache": "^5.1.1",
+ "mississippi": "^3.0.0",
+ "mkdirp": "^0.5.1",
+ "move-concurrently": "^1.0.1",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^2.6.3",
+ "ssri": "^6.0.1",
+ "unique-filename": "^1.1.1",
+ "y18n": "^4.0.0"
+ }
+ },
+ "chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "dev": true
+ },
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "ssri": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
+ "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
+ "dev": true,
+ "requires": {
+ "figgy-pudding": "^3.5.1"
+ }
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ }
+ }
+ },
+ "map-cache": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+ "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
+ "dev": true
+ },
+ "map-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
+ "dev": true,
+ "requires": {
+ "object-visit": "^1.0.0"
+ }
+ },
+ "md5.js": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+ "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
+ "dev": true,
+ "requires": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ }
+ },
+ "mdn-data": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.4.tgz",
+ "integrity": "sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA==",
+ "dev": true
+ },
+ "media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
+ "dev": true
+ },
+ "memory-fs": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
+ "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
+ "dev": true,
+ "requires": {
+ "errno": "^0.1.3",
+ "readable-stream": "^2.0.1"
+ }
+ },
+ "merge-descriptors": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
+ "dev": true
+ },
+ "merge-source-map": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz",
+ "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==",
+ "dev": true,
+ "requires": {
+ "source-map": "^0.6.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true
+ },
+ "merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true
+ },
+ "methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
+ "dev": true
+ },
+ "micromatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
+ "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
+ "dev": true,
+ "requires": {
+ "braces": "^3.0.1",
+ "picomatch": "^2.0.5"
+ }
+ },
+ "miller-rabin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
+ "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.0.0",
+ "brorand": "^1.0.1"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
+ }
+ },
+ "mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "dev": true
+ },
+ "mime-db": {
+ "version": "1.44.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
+ "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==",
+ "dev": true
+ },
+ "mime-types": {
+ "version": "2.1.27",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
+ "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
+ "dev": true,
+ "requires": {
+ "mime-db": "1.44.0"
+ }
+ },
+ "mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true
+ },
+ "mini-css-extract-plugin": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.10.0.tgz",
+ "integrity": "sha512-QgKgJBjaJhxVPwrLNqqwNS0AGkuQQ31Hp4xGXEK/P7wehEg6qmNtReHKai3zRXqY60wGVWLYcOMJK2b98aGc3A==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^1.1.0",
+ "normalize-url": "1.9.1",
+ "schema-utils": "^1.0.0",
+ "webpack-sources": "^1.1.0"
+ },
+ "dependencies": {
+ "json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "loader-utils": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
+ "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
+ "dev": true,
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^1.0.1"
+ }
+ },
+ "normalize-url": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
+ "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4.0.1",
+ "prepend-http": "^1.0.0",
+ "query-string": "^4.1.0",
+ "sort-keys": "^1.0.0"
+ }
+ },
+ "schema-utils": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+ "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.1.0",
+ "ajv-errors": "^1.0.0",
+ "ajv-keywords": "^3.1.0"
+ }
+ }
+ }
+ },
+ "minimalistic-assert": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+ "dev": true
+ },
+ "minimalistic-crypto-utils": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+ "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
+ },
+ "minipass": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.1.3.tgz",
+ "integrity": "sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg==",
+ "dev": true,
+ "requires": {
+ "yallist": "^4.0.0"
+ }
+ },
+ "minipass-collect": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz",
+ "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ }
+ },
+ "minipass-flush": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
+ "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ }
+ },
+ "minipass-pipeline": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
+ "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0"
+ }
+ },
+ "minizlib": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.0.0",
+ "yallist": "^4.0.0"
+ }
+ },
+ "mississippi": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
+ "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
+ "dev": true,
+ "requires": {
+ "concat-stream": "^1.5.0",
+ "duplexify": "^3.4.2",
+ "end-of-stream": "^1.1.0",
+ "flush-write-stream": "^1.0.0",
+ "from2": "^2.1.0",
+ "parallel-transform": "^1.1.0",
+ "pump": "^3.0.0",
+ "pumpify": "^1.3.3",
+ "stream-each": "^1.1.0",
+ "through2": "^2.0.0"
+ }
+ },
+ "mixin-deep": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
+ "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
+ "dev": true,
+ "requires": {
+ "for-in": "^1.0.2",
+ "is-extendable": "^1.0.1"
+ },
+ "dependencies": {
+ "is-extendable": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
+ "dev": true,
+ "requires": {
+ "is-plain-object": "^2.0.4"
+ }
+ }
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
+ "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.5"
+ }
+ },
+ "moment": {
+ "version": "2.18.1",
+ "resolved": "https://registry.npmjs.org/moment/-/moment-2.18.1.tgz",
+ "integrity": "sha1-w2GT3Tzhwu7SrbfIAtu8d6gbHA8="
+ },
+ "move-concurrently": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
+ "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
+ "dev": true,
+ "requires": {
+ "aproba": "^1.1.1",
+ "copy-concurrently": "^1.0.0",
+ "fs-write-stream-atomic": "^1.0.8",
+ "mkdirp": "^0.5.1",
+ "rimraf": "^2.5.4",
+ "run-queue": "^1.0.3"
+ },
+ "dependencies": {
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ }
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "multicast-dns": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz",
+ "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==",
+ "dev": true,
+ "requires": {
+ "dns-packet": "^1.3.1",
+ "thunky": "^1.0.2"
+ }
+ },
+ "multicast-dns-service-types": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
+ "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
+ "dev": true
+ },
+ "mute-stream": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
+ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
+ "dev": true
+ },
+ "nan": {
+ "version": "2.14.2",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz",
+ "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==",
+ "dev": true,
+ "optional": true
+ },
+ "nanomatch": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
+ "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "fragment-cache": "^0.2.1",
+ "is-windows": "^1.0.2",
+ "kind-of": "^6.0.2",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.1"
+ }
+ },
+ "native-request": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/native-request/-/native-request-1.0.8.tgz",
+ "integrity": "sha512-vU2JojJVelUGp6jRcLwToPoWGxSx23z/0iX+I77J3Ht17rf2INGjrhOoQnjVo60nQd8wVsgzKkPfRXBiVdD2ag==",
+ "dev": true,
+ "optional": true
+ },
+ "negotiator": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
+ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==",
+ "dev": true
+ },
+ "neo-async": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
+ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
+ "dev": true
+ },
+ "next-tick": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz",
+ "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=",
+ "dev": true
+ },
+ "ng-bootstrap": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/ng-bootstrap/-/ng-bootstrap-1.6.3.tgz",
+ "integrity": "sha1-1B/UIVTAWTQiy4PEc6OCiqdSW/U=",
+ "requires": {
+ "moment": "2.18.1"
+ }
+ },
+ "ng2-pdf-viewer": {
+ "version": "6.3.2",
+ "resolved": "https://registry.npmjs.org/ng2-pdf-viewer/-/ng2-pdf-viewer-6.3.2.tgz",
+ "integrity": "sha512-H2tBhDd+Lq6CUzK2g54HsCcZDR2wTn1sDjYqKY3yF0Ydasl2R5ppCKynZBU/zge4EKvmHglJI120FbQMpJKDYQ==",
+ "requires": {
+ "@types/pdfjs-dist": "^2.1.4",
+ "pdfjs-dist": "^2.4.456",
+ "tslib": "^1.10.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
+ }
+ }
+ },
+ "ngx-cookie-service": {
+ "version": "10.1.1",
+ "resolved": "https://registry.npmjs.org/ngx-cookie-service/-/ngx-cookie-service-10.1.1.tgz",
+ "integrity": "sha512-HvBrYHdxMN1NvFJGEIF/8EuAg2fjxj8QwqTv9h6qZGqNLU+lUba8Pb2zRPw1YA+gqKkJawOy5dYNeH0kyPyipw==",
+ "requires": {
+ "tslib": "^2.0.0"
+ }
+ },
+ "ngx-file-drop": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/ngx-file-drop/-/ngx-file-drop-10.0.0.tgz",
+ "integrity": "sha512-izV90rNuXGeTCoodvD35sCC/D/bDIuFFdj1UnTMf8n3PA5O5v+7L/PJ8d9IAXIhjAg3fY0lIIqbP8RFexMbNeA==",
+ "requires": {
+ "tslib": "^2.0.0"
+ }
+ },
+ "ngx-infinite-scroll": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/ngx-infinite-scroll/-/ngx-infinite-scroll-9.1.0.tgz",
+ "integrity": "sha512-ZulbahgFsoPmP8cz7qPGDeFX9nKiSm74aav8vXNSI1ZoPiGYY5FQd8AK+yXqygY7tyCJRyt8Wp3DIg7zgP5dPA==",
+ "requires": {
+ "@scarf/scarf": "^1.1.0",
+ "opencollective-postinstall": "^2.0.2"
+ }
+ },
+ "nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+ "dev": true
+ },
+ "node-fetch-npm": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz",
+ "integrity": "sha512-iOuIQDWDyjhv9qSDrj9aq/klt6F9z1p2otB3AV7v3zBDcL/x+OfGsvGQZZCcMZbUf4Ujw1xGNQkjvGnVT22cKg==",
+ "dev": true,
+ "requires": {
+ "encoding": "^0.1.11",
+ "json-parse-better-errors": "^1.0.0",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "node-forge": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz",
+ "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==",
+ "dev": true
+ },
+ "node-libs-browser": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
+ "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==",
+ "dev": true,
+ "requires": {
+ "assert": "^1.1.1",
+ "browserify-zlib": "^0.2.0",
+ "buffer": "^4.3.0",
+ "console-browserify": "^1.1.0",
+ "constants-browserify": "^1.0.0",
+ "crypto-browserify": "^3.11.0",
+ "domain-browser": "^1.1.1",
+ "events": "^3.0.0",
+ "https-browserify": "^1.0.0",
+ "os-browserify": "^0.3.0",
+ "path-browserify": "0.0.1",
+ "process": "^0.11.10",
+ "punycode": "^1.2.4",
+ "querystring-es3": "^0.2.0",
+ "readable-stream": "^2.3.3",
+ "stream-browserify": "^2.0.1",
+ "stream-http": "^2.7.2",
+ "string_decoder": "^1.0.0",
+ "timers-browserify": "^2.0.4",
+ "tty-browserify": "0.0.0",
+ "url": "^0.11.0",
+ "util": "^0.11.0",
+ "vm-browserify": "^1.0.1"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+ "dev": true
+ }
+ }
+ },
+ "node-releases": {
+ "version": "1.1.64",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.64.tgz",
+ "integrity": "sha512-Iec8O9166/x2HRMJyLLLWkd0sFFLrFNy+Xf+JQfSQsdBJzPcHpNl3JQ9gD4j+aJxmCa25jNsIbM4bmACtSbkSg==",
+ "dev": true
+ },
+ "normalize-package-data": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+ "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ },
+ "dependencies": {
+ "hosted-git-info": {
+ "version": "2.8.8",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
+ "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "normalize-range": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+ "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
+ "dev": true
+ },
+ "normalize-url": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-3.3.0.tgz",
+ "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==",
+ "dev": true
+ },
+ "npm-bundled": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
+ "integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
+ "dev": true,
+ "requires": {
+ "npm-normalize-package-bin": "^1.0.1"
+ }
+ },
+ "npm-install-checks": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz",
+ "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==",
+ "dev": true,
+ "requires": {
+ "semver": "^7.1.1"
+ }
+ },
+ "npm-normalize-package-bin": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
+ "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==",
+ "dev": true
+ },
+ "npm-package-arg": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.0.1.tgz",
+ "integrity": "sha512-/h5Fm6a/exByzFSTm7jAyHbgOqErl9qSNJDQF32Si/ZzgwT2TERVxRxn3Jurw1wflgyVVAxnFR4fRHPM7y1ClQ==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^3.0.2",
+ "semver": "^7.0.0",
+ "validate-npm-package-name": "^3.0.0"
+ }
+ },
+ "npm-packlist": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
+ "integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
+ "dev": true,
+ "requires": {
+ "ignore-walk": "^3.0.1",
+ "npm-bundled": "^1.0.1",
+ "npm-normalize-package-bin": "^1.0.1"
+ }
+ },
+ "npm-pick-manifest": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.0.tgz",
+ "integrity": "sha512-ygs4k6f54ZxJXrzT0x34NybRlLeZ4+6nECAIbr2i0foTnijtS1TJiyzpqtuUAJOps/hO0tNDr8fRV5g+BtRlTw==",
+ "dev": true,
+ "requires": {
+ "npm-install-checks": "^4.0.0",
+ "npm-package-arg": "^8.0.0",
+ "semver": "^7.0.0"
+ }
+ },
+ "npm-registry-fetch": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-4.0.7.tgz",
+ "integrity": "sha512-cny9v0+Mq6Tjz+e0erFAB+RYJ/AVGzkjnISiobqP8OWj9c9FLoZZu8/SPSKJWE17F1tk4018wfjV+ZbIbqC7fQ==",
+ "dev": true,
+ "requires": {
+ "JSONStream": "^1.3.4",
+ "bluebird": "^3.5.1",
+ "figgy-pudding": "^3.4.1",
+ "lru-cache": "^5.1.1",
+ "make-fetch-happen": "^5.0.0",
+ "npm-package-arg": "^6.1.0",
+ "safe-buffer": "^5.2.0"
+ },
+ "dependencies": {
+ "hosted-git-info": {
+ "version": "2.8.8",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
+ "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
+ "dev": true
+ },
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "npm-package-arg": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz",
+ "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^2.7.1",
+ "osenv": "^0.1.5",
+ "semver": "^5.6.0",
+ "validate-npm-package-name": "^3.0.0"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ }
+ }
+ },
+ "npm-run-path": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
+ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
+ "dev": true,
+ "requires": {
+ "path-key": "^2.0.0"
+ }
+ },
+ "nth-check": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
+ "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
+ "dev": true,
+ "requires": {
+ "boolbase": "~1.0.0"
+ }
+ },
+ "num2fraction": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
+ "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=",
+ "dev": true
+ },
+ "oauth-sign": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
+ "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "dev": true
+ },
+ "object-component": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/object-component/-/object-component-0.0.3.tgz",
+ "integrity": "sha1-8MaapQ78lbhmwYb0AKM3acsvEpE=",
+ "dev": true
+ },
+ "object-copy": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
+ "dev": true,
+ "requires": {
+ "copy-descriptor": "^0.1.0",
+ "define-property": "^0.2.5",
+ "kind-of": "^3.0.3"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "object-inspect": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
+ "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
+ "dev": true
+ },
+ "object-is": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.3.tgz",
+ "integrity": "sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.1"
+ }
+ },
+ "object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true
+ },
+ "object-visit": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.0"
+ }
+ },
+ "object.assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
+ "integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.18.0-next.0",
+ "has-symbols": "^1.0.1",
+ "object-keys": "^1.1.1"
+ }
+ },
+ "object.getownpropertydescriptors": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz",
+ "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.7",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
+ "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
+ }
+ },
+ "object.pick": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
+ "dev": true,
+ "requires": {
+ "isobject": "^3.0.1"
+ }
+ },
+ "object.values": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz",
+ "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.7",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
+ "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
+ }
+ },
+ "obuf": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
+ "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
+ "dev": true
+ },
+ "on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
+ "dev": true,
+ "requires": {
+ "ee-first": "1.1.1"
+ }
+ },
+ "on-headers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
+ "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^2.1.0"
+ }
+ },
+ "open": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/open/-/open-7.2.0.tgz",
+ "integrity": "sha512-4HeyhxCvBTI5uBePsAdi55C5fmqnWZ2e2MlmvWi5KW5tdH5rxoiv/aMtbeVxKZc3eWkT1GymMnLG8XC4Rq4TDQ==",
+ "dev": true,
+ "requires": {
+ "is-docker": "^2.0.0",
+ "is-wsl": "^2.1.1"
+ }
+ },
+ "opencollective-postinstall": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz",
+ "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q=="
+ },
+ "opn": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz",
+ "integrity": "sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA==",
+ "dev": true,
+ "requires": {
+ "is-wsl": "^1.1.0"
+ },
+ "dependencies": {
+ "is-wsl": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+ "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
+ "dev": true
+ }
+ }
+ },
+ "ora": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-5.0.0.tgz",
+ "integrity": "sha512-s26qdWqke2kjN/wC4dy+IQPBIMWBJlSU/0JZhk30ZDBLelW25rv66yutUWARMigpGPzcXHb+Nac5pNhN/WsARw==",
+ "dev": true,
+ "requires": {
+ "chalk": "^4.1.0",
+ "cli-cursor": "^3.1.0",
+ "cli-spinners": "^2.4.0",
+ "is-interactive": "^1.0.0",
+ "log-symbols": "^4.0.0",
+ "mute-stream": "0.0.8",
+ "strip-ansi": "^6.0.0",
+ "wcwidth": "^1.0.1"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "chalk": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+ "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ },
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "original": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz",
+ "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==",
+ "dev": true,
+ "requires": {
+ "url-parse": "^1.4.3"
+ }
+ },
+ "os-browserify": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
+ "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=",
+ "dev": true
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
+ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
+ "dev": true
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "dev": true
+ },
+ "osenv": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
+ "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
+ "dev": true,
+ "requires": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
+ }
+ },
+ "p-finally": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+ "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
+ "dev": true
+ },
+ "p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.0.0"
+ }
+ },
+ "p-map": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+ "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+ "dev": true,
+ "requires": {
+ "aggregate-error": "^3.0.0"
+ }
+ },
+ "p-retry": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-3.0.1.tgz",
+ "integrity": "sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w==",
+ "dev": true,
+ "requires": {
+ "retry": "^0.12.0"
+ }
+ },
+ "p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
+ },
+ "pacote": {
+ "version": "9.5.12",
+ "resolved": "https://registry.npmjs.org/pacote/-/pacote-9.5.12.tgz",
+ "integrity": "sha512-BUIj/4kKbwWg4RtnBncXPJd15piFSVNpTzY0rysSr3VnMowTYgkGKcaHrbReepAkjTr8lH2CVWRi58Spg2CicQ==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.5.3",
+ "cacache": "^12.0.2",
+ "chownr": "^1.1.2",
+ "figgy-pudding": "^3.5.1",
+ "get-stream": "^4.1.0",
+ "glob": "^7.1.3",
+ "infer-owner": "^1.0.4",
+ "lru-cache": "^5.1.1",
+ "make-fetch-happen": "^5.0.0",
+ "minimatch": "^3.0.4",
+ "minipass": "^2.3.5",
+ "mississippi": "^3.0.0",
+ "mkdirp": "^0.5.1",
+ "normalize-package-data": "^2.4.0",
+ "npm-normalize-package-bin": "^1.0.0",
+ "npm-package-arg": "^6.1.0",
+ "npm-packlist": "^1.1.12",
+ "npm-pick-manifest": "^3.0.0",
+ "npm-registry-fetch": "^4.0.0",
+ "osenv": "^0.1.5",
+ "promise-inflight": "^1.0.1",
+ "promise-retry": "^1.1.1",
+ "protoduck": "^5.0.1",
+ "rimraf": "^2.6.2",
+ "safe-buffer": "^5.1.2",
+ "semver": "^5.6.0",
+ "ssri": "^6.0.1",
+ "tar": "^4.4.10",
+ "unique-filename": "^1.1.1",
+ "which": "^1.3.1"
+ },
+ "dependencies": {
+ "cacache": {
+ "version": "12.0.4",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
+ "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.5.5",
+ "chownr": "^1.1.1",
+ "figgy-pudding": "^3.5.1",
+ "glob": "^7.1.4",
+ "graceful-fs": "^4.1.15",
+ "infer-owner": "^1.0.3",
+ "lru-cache": "^5.1.1",
+ "mississippi": "^3.0.0",
+ "mkdirp": "^0.5.1",
+ "move-concurrently": "^1.0.1",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^2.6.3",
+ "ssri": "^6.0.1",
+ "unique-filename": "^1.1.1",
+ "y18n": "^4.0.0"
+ }
+ },
+ "chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "dev": true
+ },
+ "fs-minipass": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
+ "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
+ "dev": true,
+ "requires": {
+ "minipass": "^2.6.0"
+ }
+ },
+ "hosted-git-info": {
+ "version": "2.8.8",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
+ "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
+ "dev": true
+ },
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "minipass": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
+ "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.0"
+ }
+ },
+ "minizlib": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
+ "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
+ "dev": true,
+ "requires": {
+ "minipass": "^2.9.0"
+ }
+ },
+ "npm-package-arg": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-6.1.1.tgz",
+ "integrity": "sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg==",
+ "dev": true,
+ "requires": {
+ "hosted-git-info": "^2.7.1",
+ "osenv": "^0.1.5",
+ "semver": "^5.6.0",
+ "validate-npm-package-name": "^3.0.0"
+ }
+ },
+ "npm-pick-manifest": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz",
+ "integrity": "sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw==",
+ "dev": true,
+ "requires": {
+ "figgy-pudding": "^3.5.1",
+ "npm-package-arg": "^6.0.0",
+ "semver": "^5.4.1"
+ }
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ },
+ "ssri": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
+ "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
+ "dev": true,
+ "requires": {
+ "figgy-pudding": "^3.5.1"
+ }
+ },
+ "tar": {
+ "version": "4.4.13",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz",
+ "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
+ "dev": true,
+ "requires": {
+ "chownr": "^1.1.1",
+ "fs-minipass": "^1.2.5",
+ "minipass": "^2.8.6",
+ "minizlib": "^1.2.1",
+ "mkdirp": "^0.5.0",
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.3"
+ }
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ }
+ }
+ },
+ "pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+ "dev": true
+ },
+ "parallel-transform": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz",
+ "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==",
+ "dev": true,
+ "requires": {
+ "cyclist": "^1.0.1",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.1.5"
+ }
+ },
+ "parse-asn1": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz",
+ "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==",
+ "dev": true,
+ "requires": {
+ "asn1.js": "^5.2.0",
+ "browserify-aes": "^1.0.0",
+ "evp_bytestokey": "^1.0.0",
+ "pbkdf2": "^3.0.3",
+ "safe-buffer": "^5.1.1"
+ }
+ },
+ "parse-json": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
+ "dev": true,
+ "requires": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ }
+ },
+ "parse5": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
+ "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==",
+ "dev": true
+ },
+ "parse5-htmlparser2-tree-adapter": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz",
+ "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==",
+ "dev": true,
+ "requires": {
+ "parse5": "^6.0.1"
+ }
+ },
+ "parseqs": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz",
+ "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=",
+ "dev": true,
+ "requires": {
+ "better-assert": "~1.0.0"
+ }
+ },
+ "parseuri": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.5.tgz",
+ "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=",
+ "dev": true,
+ "requires": {
+ "better-assert": "~1.0.0"
+ }
+ },
+ "parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true
+ },
+ "pascalcase": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
+ "dev": true
+ },
+ "path-browserify": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
+ "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==",
+ "dev": true
+ },
+ "path-dirname": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+ "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
+ "dev": true
+ },
+ "path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+ },
+ "path-is-inside": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
+ "dev": true
+ },
+ "path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+ "dev": true
+ },
+ "path-parse": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+ "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
+ },
+ "path-to-regexp": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+ "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
+ "dev": true
+ },
+ "path-type": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+ "dev": true
+ },
+ "pbkdf2": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz",
+ "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==",
+ "dev": true,
+ "requires": {
+ "create-hash": "^1.1.2",
+ "create-hmac": "^1.1.4",
+ "ripemd160": "^2.0.1",
+ "safe-buffer": "^5.0.1",
+ "sha.js": "^2.4.8"
+ }
+ },
+ "pdfjs-dist": {
+ "version": "2.5.207",
+ "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.5.207.tgz",
+ "integrity": "sha512-xGDUhnCYPfHy+unMXCLCJtlpZaaZ17Ew3WIL0tnSgKFUZXHAPD49GO9xScyszSsQMoutNDgRb+rfBXIaX/lJbw=="
+ },
+ "performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
+ "dev": true
+ },
+ "picomatch": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
+ "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
+ "dev": true
+ },
+ "pify": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
+ "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
+ "dev": true
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
+ "dev": true
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+ "dev": true,
+ "requires": {
+ "pinkie": "^2.0.0"
+ }
+ },
+ "pkg-dir": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+ "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+ "dev": true,
+ "requires": {
+ "find-up": "^3.0.0"
+ }
+ },
+ "pnp-webpack-plugin": {
+ "version": "1.6.4",
+ "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz",
+ "integrity": "sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg==",
+ "dev": true,
+ "requires": {
+ "ts-pnp": "^1.1.6"
+ }
+ },
+ "portfinder": {
+ "version": "1.0.28",
+ "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.28.tgz",
+ "integrity": "sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA==",
+ "dev": true,
+ "requires": {
+ "async": "^2.6.2",
+ "debug": "^3.1.1",
+ "mkdirp": "^0.5.5"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ },
+ "posix-character-classes": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+ "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
+ "dev": true
+ },
+ "postcss": {
+ "version": "7.0.32",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz",
+ "integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.2",
+ "source-map": "^0.6.1",
+ "supports-color": "^6.1.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "postcss-calc": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-7.0.5.tgz",
+ "integrity": "sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.27",
+ "postcss-selector-parser": "^6.0.2",
+ "postcss-value-parser": "^4.0.2"
+ }
+ },
+ "postcss-colormin": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-4.0.3.tgz",
+ "integrity": "sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.0.0",
+ "color": "^3.0.0",
+ "has": "^1.0.0",
+ "postcss": "^7.0.0",
+ "postcss-value-parser": "^3.0.0"
+ },
+ "dependencies": {
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-convert-values": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz",
+ "integrity": "sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.0",
+ "postcss-value-parser": "^3.0.0"
+ },
+ "dependencies": {
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-discard-comments": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz",
+ "integrity": "sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.0"
+ }
+ },
+ "postcss-discard-duplicates": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz",
+ "integrity": "sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.0"
+ }
+ },
+ "postcss-discard-empty": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz",
+ "integrity": "sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.0"
+ }
+ },
+ "postcss-discard-overridden": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz",
+ "integrity": "sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.0"
+ }
+ },
+ "postcss-import": {
+ "version": "12.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-12.0.1.tgz",
+ "integrity": "sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.1",
+ "postcss-value-parser": "^3.2.3",
+ "read-cache": "^1.0.0",
+ "resolve": "^1.1.7"
+ },
+ "dependencies": {
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-load-config": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.1.2.tgz",
+ "integrity": "sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw==",
+ "dev": true,
+ "requires": {
+ "cosmiconfig": "^5.0.0",
+ "import-cwd": "^2.0.0"
+ }
+ },
+ "postcss-loader": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz",
+ "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^1.1.0",
+ "postcss": "^7.0.0",
+ "postcss-load-config": "^2.0.0",
+ "schema-utils": "^1.0.0"
+ },
+ "dependencies": {
+ "json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "loader-utils": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
+ "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
+ "dev": true,
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^1.0.1"
+ }
+ },
+ "schema-utils": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+ "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.1.0",
+ "ajv-errors": "^1.0.0",
+ "ajv-keywords": "^3.1.0"
+ }
+ }
+ }
+ },
+ "postcss-merge-longhand": {
+ "version": "4.0.11",
+ "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz",
+ "integrity": "sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw==",
+ "dev": true,
+ "requires": {
+ "css-color-names": "0.0.4",
+ "postcss": "^7.0.0",
+ "postcss-value-parser": "^3.0.0",
+ "stylehacks": "^4.0.0"
+ },
+ "dependencies": {
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-merge-rules": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz",
+ "integrity": "sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.0.0",
+ "caniuse-api": "^3.0.0",
+ "cssnano-util-same-parent": "^4.0.0",
+ "postcss": "^7.0.0",
+ "postcss-selector-parser": "^3.0.0",
+ "vendors": "^1.0.0"
+ },
+ "dependencies": {
+ "postcss-selector-parser": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz",
+ "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==",
+ "dev": true,
+ "requires": {
+ "dot-prop": "^5.2.0",
+ "indexes-of": "^1.0.1",
+ "uniq": "^1.0.1"
+ }
+ }
+ }
+ },
+ "postcss-minify-font-values": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz",
+ "integrity": "sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.0",
+ "postcss-value-parser": "^3.0.0"
+ },
+ "dependencies": {
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-minify-gradients": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz",
+ "integrity": "sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q==",
+ "dev": true,
+ "requires": {
+ "cssnano-util-get-arguments": "^4.0.0",
+ "is-color-stop": "^1.0.0",
+ "postcss": "^7.0.0",
+ "postcss-value-parser": "^3.0.0"
+ },
+ "dependencies": {
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-minify-params": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz",
+ "integrity": "sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg==",
+ "dev": true,
+ "requires": {
+ "alphanum-sort": "^1.0.0",
+ "browserslist": "^4.0.0",
+ "cssnano-util-get-arguments": "^4.0.0",
+ "postcss": "^7.0.0",
+ "postcss-value-parser": "^3.0.0",
+ "uniqs": "^2.0.0"
+ },
+ "dependencies": {
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-minify-selectors": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz",
+ "integrity": "sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g==",
+ "dev": true,
+ "requires": {
+ "alphanum-sort": "^1.0.0",
+ "has": "^1.0.0",
+ "postcss": "^7.0.0",
+ "postcss-selector-parser": "^3.0.0"
+ },
+ "dependencies": {
+ "postcss-selector-parser": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz",
+ "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==",
+ "dev": true,
+ "requires": {
+ "dot-prop": "^5.2.0",
+ "indexes-of": "^1.0.1",
+ "uniq": "^1.0.1"
+ }
+ }
+ }
+ },
+ "postcss-modules-extract-imports": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz",
+ "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.5"
+ }
+ },
+ "postcss-modules-local-by-default": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz",
+ "integrity": "sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw==",
+ "dev": true,
+ "requires": {
+ "icss-utils": "^4.1.1",
+ "postcss": "^7.0.32",
+ "postcss-selector-parser": "^6.0.2",
+ "postcss-value-parser": "^4.1.0"
+ }
+ },
+ "postcss-modules-scope": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz",
+ "integrity": "sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.6",
+ "postcss-selector-parser": "^6.0.0"
+ }
+ },
+ "postcss-modules-values": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz",
+ "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==",
+ "dev": true,
+ "requires": {
+ "icss-utils": "^4.0.0",
+ "postcss": "^7.0.6"
+ }
+ },
+ "postcss-normalize-charset": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz",
+ "integrity": "sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.0"
+ }
+ },
+ "postcss-normalize-display-values": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz",
+ "integrity": "sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ==",
+ "dev": true,
+ "requires": {
+ "cssnano-util-get-match": "^4.0.0",
+ "postcss": "^7.0.0",
+ "postcss-value-parser": "^3.0.0"
+ },
+ "dependencies": {
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-normalize-positions": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz",
+ "integrity": "sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA==",
+ "dev": true,
+ "requires": {
+ "cssnano-util-get-arguments": "^4.0.0",
+ "has": "^1.0.0",
+ "postcss": "^7.0.0",
+ "postcss-value-parser": "^3.0.0"
+ },
+ "dependencies": {
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-normalize-repeat-style": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz",
+ "integrity": "sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q==",
+ "dev": true,
+ "requires": {
+ "cssnano-util-get-arguments": "^4.0.0",
+ "cssnano-util-get-match": "^4.0.0",
+ "postcss": "^7.0.0",
+ "postcss-value-parser": "^3.0.0"
+ },
+ "dependencies": {
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-normalize-string": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz",
+ "integrity": "sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.0",
+ "postcss": "^7.0.0",
+ "postcss-value-parser": "^3.0.0"
+ },
+ "dependencies": {
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-normalize-timing-functions": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz",
+ "integrity": "sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A==",
+ "dev": true,
+ "requires": {
+ "cssnano-util-get-match": "^4.0.0",
+ "postcss": "^7.0.0",
+ "postcss-value-parser": "^3.0.0"
+ },
+ "dependencies": {
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-normalize-unicode": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz",
+ "integrity": "sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.0.0",
+ "postcss": "^7.0.0",
+ "postcss-value-parser": "^3.0.0"
+ },
+ "dependencies": {
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-normalize-url": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz",
+ "integrity": "sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA==",
+ "dev": true,
+ "requires": {
+ "is-absolute-url": "^2.0.0",
+ "normalize-url": "^3.0.0",
+ "postcss": "^7.0.0",
+ "postcss-value-parser": "^3.0.0"
+ },
+ "dependencies": {
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-normalize-whitespace": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz",
+ "integrity": "sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA==",
+ "dev": true,
+ "requires": {
+ "postcss": "^7.0.0",
+ "postcss-value-parser": "^3.0.0"
+ },
+ "dependencies": {
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-ordered-values": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz",
+ "integrity": "sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw==",
+ "dev": true,
+ "requires": {
+ "cssnano-util-get-arguments": "^4.0.0",
+ "postcss": "^7.0.0",
+ "postcss-value-parser": "^3.0.0"
+ },
+ "dependencies": {
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-reduce-initial": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz",
+ "integrity": "sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.0.0",
+ "caniuse-api": "^3.0.0",
+ "has": "^1.0.0",
+ "postcss": "^7.0.0"
+ }
+ },
+ "postcss-reduce-transforms": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz",
+ "integrity": "sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg==",
+ "dev": true,
+ "requires": {
+ "cssnano-util-get-match": "^4.0.0",
+ "has": "^1.0.0",
+ "postcss": "^7.0.0",
+ "postcss-value-parser": "^3.0.0"
+ },
+ "dependencies": {
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-selector-parser": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz",
+ "integrity": "sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw==",
+ "dev": true,
+ "requires": {
+ "cssesc": "^3.0.0",
+ "indexes-of": "^1.0.1",
+ "uniq": "^1.0.1",
+ "util-deprecate": "^1.0.2"
+ }
+ },
+ "postcss-svgo": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-4.0.2.tgz",
+ "integrity": "sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw==",
+ "dev": true,
+ "requires": {
+ "is-svg": "^3.0.0",
+ "postcss": "^7.0.0",
+ "postcss-value-parser": "^3.0.0",
+ "svgo": "^1.0.0"
+ },
+ "dependencies": {
+ "postcss-value-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+ "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+ "dev": true
+ }
+ }
+ },
+ "postcss-unique-selectors": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz",
+ "integrity": "sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg==",
+ "dev": true,
+ "requires": {
+ "alphanum-sort": "^1.0.0",
+ "postcss": "^7.0.0",
+ "uniqs": "^2.0.0"
+ }
+ },
+ "postcss-value-parser": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz",
+ "integrity": "sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ==",
+ "dev": true
+ },
+ "prepend-http": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
+ "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
+ "dev": true
+ },
+ "process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
+ "dev": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true
+ },
+ "promise-inflight": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
+ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
+ "dev": true
+ },
+ "promise-retry": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-1.1.1.tgz",
+ "integrity": "sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0=",
+ "dev": true,
+ "requires": {
+ "err-code": "^1.0.0",
+ "retry": "^0.10.0"
+ },
+ "dependencies": {
+ "retry": {
+ "version": "0.10.1",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.10.1.tgz",
+ "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=",
+ "dev": true
+ }
+ }
+ },
+ "protoduck": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/protoduck/-/protoduck-5.0.1.tgz",
+ "integrity": "sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg==",
+ "dev": true,
+ "requires": {
+ "genfun": "^5.0.0"
+ }
+ },
+ "protractor": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/protractor/-/protractor-7.0.0.tgz",
+ "integrity": "sha512-UqkFjivi4GcvUQYzqGYNe0mLzfn5jiLmO8w9nMhQoJRLhy2grJonpga2IWhI6yJO30LibWXJJtA4MOIZD2GgZw==",
+ "dev": true,
+ "requires": {
+ "@types/q": "^0.0.32",
+ "@types/selenium-webdriver": "^3.0.0",
+ "blocking-proxy": "^1.0.0",
+ "browserstack": "^1.5.1",
+ "chalk": "^1.1.3",
+ "glob": "^7.0.3",
+ "jasmine": "2.8.0",
+ "jasminewd2": "^2.1.0",
+ "q": "1.4.1",
+ "saucelabs": "^1.5.0",
+ "selenium-webdriver": "3.6.0",
+ "source-map-support": "~0.4.0",
+ "webdriver-js-extender": "2.1.0",
+ "webdriver-manager": "^12.1.7",
+ "yargs": "^15.3.1"
+ },
+ "dependencies": {
+ "@types/q": {
+ "version": "0.0.32",
+ "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz",
+ "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
+ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
+ "dev": true
+ },
+ "array-union": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
+ "dev": true,
+ "requires": {
+ "array-uniq": "^1.0.1"
+ }
+ },
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^2.2.1",
+ "escape-string-regexp": "^1.0.2",
+ "has-ansi": "^2.0.0",
+ "strip-ansi": "^3.0.0",
+ "supports-color": "^2.0.0"
+ }
+ },
+ "cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ },
+ "dependencies": {
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ }
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "del": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz",
+ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=",
+ "dev": true,
+ "requires": {
+ "globby": "^5.0.0",
+ "is-path-cwd": "^1.0.0",
+ "is-path-in-cwd": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0",
+ "rimraf": "^2.2.8"
+ }
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "find-up": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
+ "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "globby": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz",
+ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=",
+ "dev": true,
+ "requires": {
+ "array-union": "^1.0.1",
+ "arrify": "^1.0.0",
+ "glob": "^7.0.3",
+ "object-assign": "^4.0.1",
+ "pify": "^2.0.0",
+ "pinkie-promise": "^2.0.0"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "is-path-cwd": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz",
+ "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=",
+ "dev": true
+ },
+ "is-path-in-cwd": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz",
+ "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==",
+ "dev": true,
+ "requires": {
+ "is-path-inside": "^1.0.0"
+ }
+ },
+ "is-path-inside": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz",
+ "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=",
+ "dev": true,
+ "requires": {
+ "path-is-inside": "^1.0.1"
+ }
+ },
+ "locate-path": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^4.1.0"
+ }
+ },
+ "p-locate": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^2.2.0"
+ }
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ },
+ "q": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/q/-/q-1.4.1.tgz",
+ "integrity": "sha1-VXBbzZPF82c1MMLCy8DCs63cKG4=",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ },
+ "source-map-support": {
+ "version": "0.4.18",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
+ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
+ "dev": true,
+ "requires": {
+ "source-map": "^0.5.6"
+ }
+ },
+ "string-width": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
+ "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ }
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
+ "dev": true
+ },
+ "webdriver-manager": {
+ "version": "12.1.7",
+ "resolved": "https://registry.npmjs.org/webdriver-manager/-/webdriver-manager-12.1.7.tgz",
+ "integrity": "sha512-XINj6b8CYuUYC93SG3xPkxlyUc3IJbD6Vvo75CVGuG9uzsefDzWQrhz0Lq8vbPxtb4d63CZdYophF8k8Or/YiA==",
+ "dev": true,
+ "requires": {
+ "adm-zip": "^0.4.9",
+ "chalk": "^1.1.1",
+ "del": "^2.2.0",
+ "glob": "^7.0.3",
+ "ini": "^1.3.4",
+ "minimist": "^1.2.0",
+ "q": "^1.4.1",
+ "request": "^2.87.0",
+ "rimraf": "^2.5.2",
+ "semver": "^5.3.0",
+ "xml2js": "^0.4.17"
+ }
+ },
+ "wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "dependencies": {
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+ "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.0"
+ }
+ }
+ }
+ },
+ "yargs": {
+ "version": "15.4.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "dev": true,
+ "requires": {
+ "cliui": "^6.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^4.1.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^4.2.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^18.1.2"
+ }
+ },
+ "yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
+ "proxy-addr": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz",
+ "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==",
+ "dev": true,
+ "requires": {
+ "forwarded": "~0.1.2",
+ "ipaddr.js": "1.9.1"
+ }
+ },
+ "prr": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+ "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
+ "dev": true
+ },
+ "psl": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
+ "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==",
+ "dev": true
+ },
+ "public-encrypt": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
+ "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
+ "dev": true,
+ "requires": {
+ "bn.js": "^4.1.0",
+ "browserify-rsa": "^4.0.0",
+ "create-hash": "^1.1.0",
+ "parse-asn1": "^5.0.0",
+ "randombytes": "^2.0.1",
+ "safe-buffer": "^5.1.2"
+ },
+ "dependencies": {
+ "bn.js": {
+ "version": "4.11.9",
+ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
+ "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==",
+ "dev": true
+ }
+ }
+ },
+ "pump": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "pumpify": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
+ "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
+ "dev": true,
+ "requires": {
+ "duplexify": "^3.6.0",
+ "inherits": "^2.0.3",
+ "pump": "^2.0.0"
+ },
+ "dependencies": {
+ "pump": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
+ "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ }
+ }
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
+ },
+ "q": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
+ "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
+ "dev": true
+ },
+ "qjobs": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz",
+ "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==",
+ "dev": true
+ },
+ "qs": {
+ "version": "6.7.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
+ "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==",
+ "dev": true
+ },
+ "query-string": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
+ "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
+ "dev": true,
+ "requires": {
+ "object-assign": "^4.1.0",
+ "strict-uri-encode": "^1.0.0"
+ }
+ },
+ "querystring": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+ "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
+ "dev": true
+ },
+ "querystring-es3": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
+ "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
+ "dev": true
+ },
+ "querystringify": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+ "dev": true
+ },
+ "randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "randomfill": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
+ "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
+ "dev": true,
+ "requires": {
+ "randombytes": "^2.0.5",
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "dev": true
+ },
+ "raw-body": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
+ "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==",
+ "dev": true,
+ "requires": {
+ "bytes": "3.1.0",
+ "http-errors": "1.7.2",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ },
+ "dependencies": {
+ "bytes": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz",
+ "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==",
+ "dev": true
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ }
+ }
+ },
+ "raw-loader": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.1.tgz",
+ "integrity": "sha512-baolhQBSi3iNh1cglJjA0mYzga+wePk7vdEX//1dTFd+v4TsQlQE0jitJSNF1OIP82rdYulH7otaVmdlDaJ64A==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^2.0.0",
+ "schema-utils": "^2.6.5"
+ }
+ },
+ "read-cache": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+ "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=",
+ "dev": true,
+ "requires": {
+ "pify": "^2.3.0"
+ },
+ "dependencies": {
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
+ "dev": true
+ }
+ }
+ },
+ "read-package-json": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.2.tgz",
+ "integrity": "sha512-D1KmuLQr6ZSJS0tW8hf3WGpRlwszJOXZ3E8Yd/DNRaM5d+1wVRZdHlpGBLAuovjr28LbWvjpWkBHMxpRGGjzNA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "normalize-package-data": "^2.0.0",
+ "npm-normalize-package-bin": "^1.0.0"
+ }
+ },
+ "read-package-tree": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/read-package-tree/-/read-package-tree-5.3.1.tgz",
+ "integrity": "sha512-mLUDsD5JVtlZxjSlPPx1RETkNjjvQYuweKwNVt1Sn8kP5Jh44pvYuUHCp6xSVDZWbNxVxG5lyZJ921aJH61sTw==",
+ "dev": true,
+ "requires": {
+ "read-package-json": "^2.0.0",
+ "readdir-scoped-modules": "^1.0.0",
+ "util-promisify": "^2.1.0"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.7",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
+ "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
+ "dev": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "readdir-scoped-modules": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz",
+ "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==",
+ "dev": true,
+ "requires": {
+ "debuglog": "^1.0.1",
+ "dezalgo": "^1.0.0",
+ "graceful-fs": "^4.1.2",
+ "once": "^1.3.0"
+ }
+ },
+ "readdirp": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
+ "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
+ "dev": true,
+ "requires": {
+ "picomatch": "^2.2.1"
+ }
+ },
+ "reflect-metadata": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz",
+ "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==",
+ "dev": true
+ },
+ "regenerate": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz",
+ "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==",
+ "dev": true
+ },
+ "regenerate-unicode-properties": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz",
+ "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==",
+ "dev": true,
+ "requires": {
+ "regenerate": "^1.4.0"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.13.7",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
+ "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==",
+ "dev": true
+ },
+ "regenerator-transform": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz",
+ "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==",
+ "dev": true,
+ "requires": {
+ "@babel/runtime": "^7.8.4"
+ }
+ },
+ "regex-not": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
+ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "regex-parser": {
+ "version": "2.2.11",
+ "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.2.11.tgz",
+ "integrity": "sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q==",
+ "dev": true
+ },
+ "regexp.prototype.flags": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz",
+ "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.7",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
+ "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
+ }
+ },
+ "regexpu-core": {
+ "version": "4.7.1",
+ "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz",
+ "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==",
+ "dev": true,
+ "requires": {
+ "regenerate": "^1.4.0",
+ "regenerate-unicode-properties": "^8.2.0",
+ "regjsgen": "^0.5.1",
+ "regjsparser": "^0.6.4",
+ "unicode-match-property-ecmascript": "^1.0.4",
+ "unicode-match-property-value-ecmascript": "^1.2.0"
+ }
+ },
+ "regjsgen": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz",
+ "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==",
+ "dev": true
+ },
+ "regjsparser": {
+ "version": "0.6.4",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz",
+ "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==",
+ "dev": true,
+ "requires": {
+ "jsesc": "~0.5.0"
+ },
+ "dependencies": {
+ "jsesc": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
+ "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
+ "dev": true
+ }
+ }
+ },
+ "remove-trailing-separator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+ "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
+ "dev": true
+ },
+ "repeat-element": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+ "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
+ "dev": true
+ },
+ "repeat-string": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
+ "dev": true
+ },
+ "request": {
+ "version": "2.88.2",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
+ "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
+ "dev": true,
+ "requires": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "har-validator": "~5.1.3",
+ "http-signature": "~1.2.0",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "oauth-sign": "~0.9.0",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.2",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "~2.5.0",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^3.3.2"
+ },
+ "dependencies": {
+ "qs": {
+ "version": "6.5.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
+ "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
+ "dev": true
+ },
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "dev": true
+ }
+ }
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I="
+ },
+ "require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
+ },
+ "requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
+ "dev": true
+ },
+ "resolve": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+ "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ },
+ "resolve-cwd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
+ "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
+ "dev": true,
+ "requires": {
+ "resolve-from": "^3.0.0"
+ }
+ },
+ "resolve-from": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+ "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
+ "dev": true
+ },
+ "resolve-url": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
+ "dev": true
+ },
+ "resolve-url-loader": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-3.1.2.tgz",
+ "integrity": "sha512-QEb4A76c8Mi7I3xNKXlRKQSlLBwjUV/ULFMP+G7n3/7tJZ8MG5wsZ3ucxP1Jz8Vevn6fnJsxDx9cIls+utGzPQ==",
+ "dev": true,
+ "requires": {
+ "adjust-sourcemap-loader": "3.0.0",
+ "camelcase": "5.3.1",
+ "compose-function": "3.0.3",
+ "convert-source-map": "1.7.0",
+ "es6-iterator": "2.0.3",
+ "loader-utils": "1.2.3",
+ "postcss": "7.0.21",
+ "rework": "1.0.1",
+ "rework-visit": "1.0.0",
+ "source-map": "0.6.1"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ },
+ "emojis-list": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
+ "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
+ "dev": true
+ },
+ "json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "loader-utils": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
+ "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
+ "dev": true,
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^2.0.0",
+ "json5": "^1.0.1"
+ }
+ },
+ "postcss": {
+ "version": "7.0.21",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.21.tgz",
+ "integrity": "sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.2",
+ "source-map": "^0.6.1",
+ "supports-color": "^6.1.0"
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dev": true,
+ "requires": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "ret": {
+ "version": "0.1.15",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
+ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
+ "dev": true
+ },
+ "retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
+ "dev": true
+ },
+ "reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true
+ },
+ "rework": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/rework/-/rework-1.0.1.tgz",
+ "integrity": "sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc=",
+ "dev": true,
+ "requires": {
+ "convert-source-map": "^0.3.3",
+ "css": "^2.0.0"
+ },
+ "dependencies": {
+ "convert-source-map": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz",
+ "integrity": "sha1-8dgClQr33SYxof6+BZZVDIarMZA=",
+ "dev": true
+ }
+ }
+ },
+ "rework-visit": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/rework-visit/-/rework-visit-1.0.0.tgz",
+ "integrity": "sha1-mUWygD8hni96ygCtuLyfZA+ELJo=",
+ "dev": true
+ },
+ "rfdc": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.4.tgz",
+ "integrity": "sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug==",
+ "dev": true
+ },
+ "rgb-regex": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz",
+ "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=",
+ "dev": true
+ },
+ "rgba-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz",
+ "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "ripemd160": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
+ "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
+ "dev": true,
+ "requires": {
+ "hash-base": "^3.0.0",
+ "inherits": "^2.0.1"
+ }
+ },
+ "rollup": {
+ "version": "2.26.5",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.26.5.tgz",
+ "integrity": "sha512-rCyFG3ZtQdnn9YwfuAVH0l/Om34BdO5lwCA0W6Hq+bNB21dVEBbCRxhaHOmu1G7OBFDWytbzAC104u7rxHwGjA==",
+ "dev": true,
+ "requires": {
+ "fsevents": "~2.1.2"
+ }
+ },
+ "run-async": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
+ "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
+ "dev": true
+ },
+ "run-parallel": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz",
+ "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==",
+ "dev": true
+ },
+ "run-queue": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
+ "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
+ "dev": true,
+ "requires": {
+ "aproba": "^1.1.1"
+ }
+ },
+ "rxjs": {
+ "version": "6.6.3",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz",
+ "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==",
+ "requires": {
+ "tslib": "^1.9.0"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.0.tgz",
+ "integrity": "sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw=="
+ }
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
+ "safe-regex": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
+ "dev": true,
+ "requires": {
+ "ret": "~0.1.10"
+ }
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true
+ },
+ "sass": {
+ "version": "1.26.10",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.26.10.tgz",
+ "integrity": "sha512-bzN0uvmzfsTvjz0qwccN1sPm2HxxpNI/Xa+7PlUEMS+nQvbyuEK7Y0qFqxlPHhiNHb1Ze8WQJtU31olMObkAMw==",
+ "dev": true,
+ "requires": {
+ "chokidar": ">=2.0.0 <4.0.0"
+ }
+ },
+ "sass-loader": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.0.1.tgz",
+ "integrity": "sha512-b2PSldKVTS3JcFPHSrEXh3BeAfR7XknGiGCAO5aHruR3Pf3kqLP3Gb2ypXLglRrAzgZkloNxLZ7GXEGDX0hBUQ==",
+ "dev": true,
+ "requires": {
+ "klona": "^2.0.3",
+ "loader-utils": "^2.0.0",
+ "neo-async": "^2.6.2",
+ "schema-utils": "^2.7.0",
+ "semver": "^7.3.2"
+ }
+ },
+ "saucelabs": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz",
+ "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==",
+ "dev": true,
+ "requires": {
+ "https-proxy-agent": "^2.2.1"
+ }
+ },
+ "sax": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+ "dev": true
+ },
+ "schema-utils": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
+ "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.5",
+ "ajv": "^6.12.4",
+ "ajv-keywords": "^3.5.2"
+ }
+ },
+ "select-hose": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
+ "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=",
+ "dev": true
+ },
+ "selenium-webdriver": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz",
+ "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==",
+ "dev": true,
+ "requires": {
+ "jszip": "^3.1.3",
+ "rimraf": "^2.5.4",
+ "tmp": "0.0.30",
+ "xml2js": "^0.4.17"
+ },
+ "dependencies": {
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "tmp": {
+ "version": "0.0.30",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz",
+ "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.1"
+ }
+ }
+ }
+ },
+ "selfsigned": {
+ "version": "1.10.8",
+ "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.8.tgz",
+ "integrity": "sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w==",
+ "dev": true,
+ "requires": {
+ "node-forge": "^0.10.0"
+ }
+ },
+ "semver": {
+ "version": "7.3.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz",
+ "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==",
+ "dev": true
+ },
+ "semver-dsl": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/semver-dsl/-/semver-dsl-1.0.1.tgz",
+ "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=",
+ "dev": true,
+ "requires": {
+ "semver": "^5.3.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "semver-intersect": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/semver-intersect/-/semver-intersect-1.4.0.tgz",
+ "integrity": "sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ==",
+ "dev": true,
+ "requires": {
+ "semver": "^5.0.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
+ "send": {
+ "version": "0.17.1",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz",
+ "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==",
+ "dev": true,
+ "requires": {
+ "debug": "2.6.9",
+ "depd": "~1.1.2",
+ "destroy": "~1.0.4",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "~1.7.2",
+ "mime": "1.6.0",
+ "ms": "2.1.1",
+ "on-finished": "~2.3.0",
+ "range-parser": "~1.2.1",
+ "statuses": "~1.5.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ }
+ }
+ },
+ "serialize-javascript": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
+ "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
+ "dev": true,
+ "requires": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "serve-index": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
+ "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
+ "dev": true,
+ "requires": {
+ "accepts": "~1.3.4",
+ "batch": "0.6.1",
+ "debug": "2.6.9",
+ "escape-html": "~1.0.3",
+ "http-errors": "~1.6.2",
+ "mime-types": "~2.1.17",
+ "parseurl": "~1.3.2"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "http-errors": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+ "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
+ "dev": true,
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.0",
+ "statuses": ">= 1.4.0 < 2"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "setprototypeof": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
+ "dev": true
+ }
+ }
+ },
+ "serve-static": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz",
+ "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==",
+ "dev": true,
+ "requires": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "0.17.1"
+ }
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
+ },
+ "set-immediate-shim": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
+ "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=",
+ "dev": true
+ },
+ "set-value": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
+ "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-extendable": "^0.1.1",
+ "is-plain-object": "^2.0.3",
+ "split-string": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
+ "dev": true
+ },
+ "setprototypeof": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
+ "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==",
+ "dev": true
+ },
+ "sha.js": {
+ "version": "2.4.11",
+ "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^1.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+ "dev": true
+ },
+ "signal-exit": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz",
+ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==",
+ "dev": true
+ },
+ "simple-swizzle": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+ "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.3.1"
+ },
+ "dependencies": {
+ "is-arrayish": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
+ "dev": true
+ }
+ }
+ },
+ "slash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+ "dev": true
+ },
+ "smart-buffer": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz",
+ "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==",
+ "dev": true
+ },
+ "snapdragon": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
+ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
+ "dev": true,
+ "requires": {
+ "base": "^0.11.1",
+ "debug": "^2.2.0",
+ "define-property": "^0.2.5",
+ "extend-shallow": "^2.0.1",
+ "map-cache": "^0.2.2",
+ "source-map": "^0.5.6",
+ "source-map-resolve": "^0.5.0",
+ "use": "^3.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ },
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+ "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+ "dev": true
+ }
+ }
+ },
+ "snapdragon-node": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
+ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.0",
+ "snapdragon-util": "^3.0.1"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^1.0.0"
+ }
+ },
+ "is-accessor-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-data-descriptor": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^6.0.0"
+ }
+ },
+ "is-descriptor": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "dev": true,
+ "requires": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ }
+ }
+ }
+ },
+ "snapdragon-util": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
+ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.2.0"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "socket.io": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.3.0.tgz",
+ "integrity": "sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg==",
+ "dev": true,
+ "requires": {
+ "debug": "~4.1.0",
+ "engine.io": "~3.4.0",
+ "has-binary2": "~1.0.2",
+ "socket.io-adapter": "~1.1.0",
+ "socket.io-client": "2.3.0",
+ "socket.io-parser": "~3.4.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ },
+ "socket.io-adapter": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
+ "integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g==",
+ "dev": true
+ },
+ "socket.io-client": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
+ "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
+ "dev": true,
+ "requires": {
+ "backo2": "1.0.2",
+ "base64-arraybuffer": "0.1.5",
+ "component-bind": "1.0.0",
+ "component-emitter": "1.2.1",
+ "debug": "~4.1.0",
+ "engine.io-client": "~3.4.0",
+ "has-binary2": "~1.0.2",
+ "has-cors": "1.1.0",
+ "indexof": "0.0.1",
+ "object-component": "0.0.3",
+ "parseqs": "0.0.5",
+ "parseuri": "0.0.5",
+ "socket.io-parser": "~3.3.0",
+ "to-array": "0.1.4"
+ },
+ "dependencies": {
+ "base64-arraybuffer": {
+ "version": "0.1.5",
+ "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
+ "integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=",
+ "dev": true
+ },
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
+ "dev": true
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "isarray": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
+ "dev": true
+ },
+ "socket.io-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz",
+ "integrity": "sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==",
+ "dev": true,
+ "requires": {
+ "component-emitter": "~1.3.0",
+ "debug": "~3.1.0",
+ "isarray": "2.0.1"
+ },
+ "dependencies": {
+ "component-emitter": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
+ "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
+ "dev": true
+ },
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ }
+ }
+ }
+ }
+ },
+ "socket.io-parser": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz",
+ "integrity": "sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A==",
+ "dev": true,
+ "requires": {
+ "component-emitter": "1.2.1",
+ "debug": "~4.1.0",
+ "isarray": "2.0.1"
+ },
+ "dependencies": {
+ "component-emitter": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz",
+ "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=",
+ "dev": true
+ },
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "isarray": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
+ "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=",
+ "dev": true
+ }
+ }
+ },
+ "sockjs": {
+ "version": "0.3.20",
+ "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz",
+ "integrity": "sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA==",
+ "dev": true,
+ "requires": {
+ "faye-websocket": "^0.10.0",
+ "uuid": "^3.4.0",
+ "websocket-driver": "0.6.5"
+ },
+ "dependencies": {
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "dev": true
+ }
+ }
+ },
+ "sockjs-client": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/sockjs-client/-/sockjs-client-1.4.0.tgz",
+ "integrity": "sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g==",
+ "dev": true,
+ "requires": {
+ "debug": "^3.2.5",
+ "eventsource": "^1.0.7",
+ "faye-websocket": "~0.11.1",
+ "inherits": "^2.0.3",
+ "json3": "^3.3.2",
+ "url-parse": "^1.4.3"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "faye-websocket": {
+ "version": "0.11.3",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.3.tgz",
+ "integrity": "sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA==",
+ "dev": true,
+ "requires": {
+ "websocket-driver": ">=0.5.1"
+ }
+ }
+ }
+ },
+ "socks": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.3.3.tgz",
+ "integrity": "sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA==",
+ "dev": true,
+ "requires": {
+ "ip": "1.1.5",
+ "smart-buffer": "^4.1.0"
+ }
+ },
+ "socks-proxy-agent": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz",
+ "integrity": "sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg==",
+ "dev": true,
+ "requires": {
+ "agent-base": "~4.2.1",
+ "socks": "~2.3.2"
+ },
+ "dependencies": {
+ "agent-base": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
+ "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
+ "dev": true,
+ "requires": {
+ "es6-promisify": "^5.0.0"
+ }
+ }
+ }
+ },
+ "sort-keys": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
+ "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=",
+ "dev": true,
+ "requires": {
+ "is-plain-obj": "^1.0.0"
+ }
+ },
+ "source-list-map": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
+ "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==",
+ "dev": true
+ },
+ "source-map": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+ "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
+ "dev": true
+ },
+ "source-map-loader": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-1.0.2.tgz",
+ "integrity": "sha512-oX8d6ndRjN+tVyjj6PlXSyFPhDdVAPsZA30nD3/II8g4uOv8fCz0DMn5sy8KtVbDfKQxOpGwGJnK3xIW3tauDw==",
+ "dev": true,
+ "requires": {
+ "data-urls": "^2.0.0",
+ "iconv-lite": "^0.6.2",
+ "loader-utils": "^2.0.0",
+ "schema-utils": "^2.7.0",
+ "source-map": "^0.6.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "source-map-resolve": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
+ "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
+ "dev": true,
+ "requires": {
+ "atob": "^2.1.2",
+ "decode-uri-component": "^0.2.0",
+ "resolve-url": "^0.2.1",
+ "source-map-url": "^0.4.0",
+ "urix": "^0.1.0"
+ }
+ },
+ "source-map-support": {
+ "version": "0.5.19",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz",
+ "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==",
+ "dev": true,
+ "requires": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "source-map-url": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
+ "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
+ "dev": true
+ },
+ "sourcemap-codec": {
+ "version": "1.4.8",
+ "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+ "dev": true
+ },
+ "spdx-correct": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+ "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+ "dev": true,
+ "requires": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-exceptions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+ "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+ "dev": true
+ },
+ "spdx-expression-parse": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "dev": true,
+ "requires": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "spdx-license-ids": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz",
+ "integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==",
+ "dev": true
+ },
+ "spdy": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz",
+ "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "handle-thing": "^2.0.0",
+ "http-deceiver": "^1.2.7",
+ "select-hose": "^2.0.0",
+ "spdy-transport": "^3.0.0"
+ }
+ },
+ "spdy-transport": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz",
+ "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "detect-node": "^2.0.4",
+ "hpack.js": "^2.1.6",
+ "obuf": "^1.1.2",
+ "readable-stream": "^3.0.6",
+ "wbuf": "^1.7.3"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
+ "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
+ "dev": true,
+ "requires": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ }
+ }
+ }
+ },
+ "speed-measure-webpack-plugin": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.3.tgz",
+ "integrity": "sha512-2ljD4Ch/rz2zG3HsLsnPfp23osuPBS0qPuz9sGpkNXTN1Ic4M+W9xB8l8rS8ob2cO4b1L+WTJw/0AJwWYVgcxQ==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.0.1"
+ }
+ },
+ "split-string": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
+ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^3.0.0"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "sshpk": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
+ "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
+ "dev": true,
+ "requires": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ }
+ },
+ "ssri": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.0.tgz",
+ "integrity": "sha512-aq/pz989nxVYwn16Tsbj1TqFpD5LLrQxHf5zaHuieFV+R0Bbr4y8qUsOA45hXT/N4/9UNXTarBjnjVmjSOVaAA==",
+ "dev": true,
+ "requires": {
+ "minipass": "^3.1.1"
+ }
+ },
+ "stable": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
+ "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
+ "dev": true
+ },
+ "static-extend": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
+ "dev": true,
+ "requires": {
+ "define-property": "^0.2.5",
+ "object-copy": "^0.1.0"
+ },
+ "dependencies": {
+ "define-property": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+ "dev": true,
+ "requires": {
+ "is-descriptor": "^0.1.0"
+ }
+ }
+ }
+ },
+ "statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
+ "dev": true
+ },
+ "stream-browserify": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
+ "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==",
+ "dev": true,
+ "requires": {
+ "inherits": "~2.0.1",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "stream-each": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz",
+ "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==",
+ "dev": true,
+ "requires": {
+ "end-of-stream": "^1.1.0",
+ "stream-shift": "^1.0.0"
+ }
+ },
+ "stream-http": {
+ "version": "2.8.3",
+ "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz",
+ "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==",
+ "dev": true,
+ "requires": {
+ "builtin-status-codes": "^3.0.0",
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.3.6",
+ "to-arraybuffer": "^1.0.0",
+ "xtend": "^4.0.0"
+ }
+ },
+ "stream-shift": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
+ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
+ "dev": true
+ },
+ "streamroller": {
+ "version": "2.2.4",
+ "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-2.2.4.tgz",
+ "integrity": "sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ==",
+ "dev": true,
+ "requires": {
+ "date-format": "^2.1.0",
+ "debug": "^4.1.1",
+ "fs-extra": "^8.1.0"
+ },
+ "dependencies": {
+ "date-format": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/date-format/-/date-format-2.1.0.tgz",
+ "integrity": "sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA==",
+ "dev": true
+ },
+ "fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ }
+ }
+ },
+ "strict-uri-encode": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
+ "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "string.prototype.trimend": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
+ "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.7",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
+ "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
+ }
+ },
+ "string.prototype.trimstart": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
+ "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.5"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.7",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
+ "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-eof": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+ "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
+ "dev": true
+ },
+ "style-loader": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.2.1.tgz",
+ "integrity": "sha512-ByHSTQvHLkWE9Ir5+lGbVOXhxX10fbprhLvdg96wedFZb4NDekDPxVKv5Fwmio+QcMlkkNfuK+5W1peQ5CUhZg==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^2.0.0",
+ "schema-utils": "^2.6.6"
+ }
+ },
+ "stylehacks": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz",
+ "integrity": "sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g==",
+ "dev": true,
+ "requires": {
+ "browserslist": "^4.0.0",
+ "postcss": "^7.0.0",
+ "postcss-selector-parser": "^3.0.0"
+ },
+ "dependencies": {
+ "postcss-selector-parser": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz",
+ "integrity": "sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA==",
+ "dev": true,
+ "requires": {
+ "dot-prop": "^5.2.0",
+ "indexes-of": "^1.0.1",
+ "uniq": "^1.0.1"
+ }
+ }
+ }
+ },
+ "stylus": {
+ "version": "0.54.8",
+ "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.54.8.tgz",
+ "integrity": "sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg==",
+ "dev": true,
+ "requires": {
+ "css-parse": "~2.0.0",
+ "debug": "~3.1.0",
+ "glob": "^7.1.6",
+ "mkdirp": "~1.0.4",
+ "safer-buffer": "^2.1.2",
+ "sax": "~1.2.4",
+ "semver": "^6.3.0",
+ "source-map": "^0.7.3"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ }
+ }
+ },
+ "stylus-loader": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-3.0.2.tgz",
+ "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^1.0.2",
+ "lodash.clonedeep": "^4.5.0",
+ "when": "~3.6.x"
+ },
+ "dependencies": {
+ "json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "loader-utils": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
+ "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
+ "dev": true,
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^1.0.1"
+ }
+ }
+ }
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "svgo": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz",
+ "integrity": "sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.4.1",
+ "coa": "^2.0.2",
+ "css-select": "^2.0.0",
+ "css-select-base-adapter": "^0.1.1",
+ "css-tree": "1.0.0-alpha.37",
+ "csso": "^4.0.2",
+ "js-yaml": "^3.13.1",
+ "mkdirp": "~0.5.1",
+ "object.values": "^1.1.0",
+ "sax": "~1.2.4",
+ "stable": "^0.1.8",
+ "unquote": "~1.1.1",
+ "util.promisify": "~1.0.0"
+ }
+ },
+ "symbol-observable": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
+ "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==",
+ "dev": true
+ },
+ "tapable": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
+ "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+ "dev": true
+ },
+ "tar": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.0.5.tgz",
+ "integrity": "sha512-0b4HOimQHj9nXNEAA7zWwMM91Zhhba3pspja6sQbgTpynOJf+bkjBnfybNYzbpLbnwXnbyB4LOREvlyXLkCHSg==",
+ "dev": true,
+ "requires": {
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.0.0",
+ "minipass": "^3.0.0",
+ "minizlib": "^2.1.1",
+ "mkdirp": "^1.0.3",
+ "yallist": "^4.0.0"
+ },
+ "dependencies": {
+ "mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true
+ }
+ }
+ },
+ "terser": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.3.0.tgz",
+ "integrity": "sha512-XTT3D3AwxC54KywJijmY2mxZ8nJiEjBHVYzq8l9OaYuRFWeQNBwvipuzzYEP4e+/AVcd1hqG/CqgsdIRyT45Fg==",
+ "dev": true,
+ "requires": {
+ "commander": "^2.20.0",
+ "source-map": "~0.6.1",
+ "source-map-support": "~0.5.12"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "terser-webpack-plugin": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-4.1.0.tgz",
+ "integrity": "sha512-0ZWDPIP8BtEDZdChbufcXUigOYk6dOX/P/X0hWxqDDcVAQLb8Yy/0FAaemSfax3PAA67+DJR778oz8qVbmy4hA==",
+ "dev": true,
+ "requires": {
+ "cacache": "^15.0.5",
+ "find-cache-dir": "^3.3.1",
+ "jest-worker": "^26.3.0",
+ "p-limit": "^3.0.2",
+ "schema-utils": "^2.6.6",
+ "serialize-javascript": "^4.0.0",
+ "source-map": "^0.6.1",
+ "terser": "^5.0.0",
+ "webpack-sources": "^1.4.3"
+ },
+ "dependencies": {
+ "p-limit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz",
+ "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==",
+ "dev": true,
+ "requires": {
+ "p-try": "^2.0.0"
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+ "dev": true
+ },
+ "through2": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+ "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "dev": true,
+ "requires": {
+ "readable-stream": "~2.3.6",
+ "xtend": "~4.0.1"
+ }
+ },
+ "thunky": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
+ "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==",
+ "dev": true
+ },
+ "timers-browserify": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz",
+ "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==",
+ "dev": true,
+ "requires": {
+ "setimmediate": "^1.0.4"
+ }
+ },
+ "timsort": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
+ "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
+ "dev": true
+ },
+ "tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.2"
+ }
+ },
+ "to-array": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/to-array/-/to-array-0.1.4.tgz",
+ "integrity": "sha1-F+bBH3PdTz10zaek/zI46a2b+JA=",
+ "dev": true
+ },
+ "to-arraybuffer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
+ "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=",
+ "dev": true
+ },
+ "to-fast-properties": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
+ "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
+ },
+ "to-object-path": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "to-regex": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
+ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
+ "dev": true,
+ "requires": {
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "regex-not": "^1.0.2",
+ "safe-regex": "^1.1.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "toidentifier": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz",
+ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
+ "dev": true
+ },
+ "tough-cookie": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
+ "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+ "dev": true,
+ "requires": {
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ }
+ },
+ "tr46": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz",
+ "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.1"
+ }
+ },
+ "tree-kill": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+ "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+ "dev": true
+ },
+ "ts-node": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz",
+ "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==",
+ "dev": true,
+ "requires": {
+ "arg": "^4.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "source-map-support": "^0.5.6",
+ "yn": "^3.0.0"
+ }
+ },
+ "ts-pnp": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.2.0.tgz",
+ "integrity": "sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==",
+ "dev": true
+ },
+ "tslib": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.2.tgz",
+ "integrity": "sha512-wAH28hcEKwna96/UacuWaVspVLkg4x1aDM9JlzqaQTOFczCktkVAb5fmXChgandR1EraDPs2w8P+ozM+oafwxg=="
+ },
+ "tslint": {
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz",
+ "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "builtin-modules": "^1.1.1",
+ "chalk": "^2.3.0",
+ "commander": "^2.12.1",
+ "diff": "^4.0.1",
+ "glob": "^7.1.1",
+ "js-yaml": "^3.13.1",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.3",
+ "resolve": "^1.3.2",
+ "semver": "^5.3.0",
+ "tslib": "^1.13.0",
+ "tsutils": "^2.29.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ },
+ "tslib": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.0.tgz",
+ "integrity": "sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw==",
+ "dev": true
+ }
+ }
+ },
+ "tsutils": {
+ "version": "2.29.0",
+ "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz",
+ "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.8.1"
+ },
+ "dependencies": {
+ "tslib": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.0.tgz",
+ "integrity": "sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw==",
+ "dev": true
+ }
+ }
+ },
+ "tty-browserify": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
+ "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
+ "dev": true
+ },
+ "tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+ "dev": true
+ },
+ "type": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
+ "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==",
+ "dev": true
+ },
+ "type-fest": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz",
+ "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==",
+ "dev": true
+ },
+ "type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "dev": true,
+ "requires": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ }
+ },
+ "typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+ "dev": true
+ },
+ "typescript": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz",
+ "integrity": "sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==",
+ "dev": true
+ },
+ "ua-parser-js": {
+ "version": "0.7.21",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz",
+ "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==",
+ "dev": true
+ },
+ "unicode-canonical-property-names-ecmascript": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
+ "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==",
+ "dev": true
+ },
+ "unicode-match-property-ecmascript": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz",
+ "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==",
+ "dev": true,
+ "requires": {
+ "unicode-canonical-property-names-ecmascript": "^1.0.4",
+ "unicode-property-aliases-ecmascript": "^1.0.4"
+ }
+ },
+ "unicode-match-property-value-ecmascript": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz",
+ "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==",
+ "dev": true
+ },
+ "unicode-property-aliases-ecmascript": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz",
+ "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==",
+ "dev": true
+ },
+ "union-value": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
+ "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+ "dev": true,
+ "requires": {
+ "arr-union": "^3.1.0",
+ "get-value": "^2.0.6",
+ "is-extendable": "^0.1.1",
+ "set-value": "^2.0.1"
+ }
+ },
+ "uniq": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
+ "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
+ "dev": true
+ },
+ "uniqs": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz",
+ "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=",
+ "dev": true
+ },
+ "unique-filename": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
+ "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
+ "dev": true,
+ "requires": {
+ "unique-slug": "^2.0.0"
+ }
+ },
+ "unique-slug": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz",
+ "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==",
+ "dev": true,
+ "requires": {
+ "imurmurhash": "^0.1.4"
+ }
+ },
+ "universal-analytics": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/universal-analytics/-/universal-analytics-0.4.23.tgz",
+ "integrity": "sha512-lgMIH7XBI6OgYn1woDEmxhGdj8yDefMKg7GkWdeATAlQZFrMrNyxSkpDzY57iY0/6fdlzTbBV03OawvvzG+q7A==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "request": "^2.88.2",
+ "uuid": "^3.0.0"
+ },
+ "dependencies": {
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "dev": true
+ }
+ }
+ },
+ "universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "dev": true
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
+ "dev": true
+ },
+ "unquote": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz",
+ "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=",
+ "dev": true
+ },
+ "unset-value": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+ "dev": true,
+ "requires": {
+ "has-value": "^0.3.1",
+ "isobject": "^3.0.0"
+ },
+ "dependencies": {
+ "has-value": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
+ "dev": true,
+ "requires": {
+ "get-value": "^2.0.3",
+ "has-values": "^0.1.4",
+ "isobject": "^2.0.0"
+ },
+ "dependencies": {
+ "isobject": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+ "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
+ "dev": true,
+ "requires": {
+ "isarray": "1.0.0"
+ }
+ }
+ }
+ },
+ "has-values": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+ "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
+ "dev": true
+ }
+ }
+ },
+ "upath": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
+ "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
+ "dev": true
+ },
+ "uri-js": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
+ "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "urix": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+ "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+ "dev": true
+ },
+ "url": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+ "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+ "dev": true,
+ "requires": {
+ "punycode": "1.3.2",
+ "querystring": "0.2.0"
+ },
+ "dependencies": {
+ "punycode": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+ "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
+ "dev": true
+ }
+ }
+ },
+ "url-parse": {
+ "version": "1.4.7",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz",
+ "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==",
+ "dev": true,
+ "requires": {
+ "querystringify": "^2.1.1",
+ "requires-port": "^1.0.0"
+ }
+ },
+ "use": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
+ "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+ "dev": true
+ },
+ "util": {
+ "version": "0.11.1",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
+ "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==",
+ "dev": true,
+ "requires": {
+ "inherits": "2.0.3"
+ },
+ "dependencies": {
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+ "dev": true
+ }
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
+ "dev": true
+ },
+ "util-promisify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/util-promisify/-/util-promisify-2.1.0.tgz",
+ "integrity": "sha1-PCI2R2xNMsX/PEcAKt18E7moKlM=",
+ "dev": true,
+ "requires": {
+ "object.getownpropertydescriptors": "^2.0.3"
+ }
+ },
+ "util.promisify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.1.tgz",
+ "integrity": "sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.2",
+ "has-symbols": "^1.0.1",
+ "object.getownpropertydescriptors": "^2.1.0"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.7",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
+ "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
+ "dev": true,
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.2.2",
+ "is-regex": "^1.1.1",
+ "object-inspect": "^1.8.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.1",
+ "string.prototype.trimend": "^1.0.1",
+ "string.prototype.trimstart": "^1.0.1"
+ }
+ }
+ }
+ },
+ "utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
+ "dev": true
+ },
+ "uuid": {
+ "version": "8.3.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz",
+ "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg=="
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "requires": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "validate-npm-package-name": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz",
+ "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=",
+ "dev": true,
+ "requires": {
+ "builtins": "^1.0.3"
+ }
+ },
+ "vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
+ "dev": true
+ },
+ "vendors": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.4.tgz",
+ "integrity": "sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==",
+ "dev": true
+ },
+ "verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+ "dev": true,
+ "requires": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "vm-browserify": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
+ "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
+ "dev": true
+ },
+ "void-elements": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
+ "integrity": "sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=",
+ "dev": true
+ },
+ "watchpack": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.7.4.tgz",
+ "integrity": "sha512-aWAgTW4MoSJzZPAicljkO1hsi1oKj/RRq/OJQh2PKI2UKL04c2Bs+MBOB+BBABHTXJpf9mCwHN7ANCvYsvY2sg==",
+ "dev": true,
+ "requires": {
+ "chokidar": "^3.4.1",
+ "graceful-fs": "^4.1.2",
+ "neo-async": "^2.5.0",
+ "watchpack-chokidar2": "^2.0.0"
+ }
+ },
+ "watchpack-chokidar2": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.0.tgz",
+ "integrity": "sha512-9TyfOyN/zLUbA288wZ8IsMZ+6cbzvsNyEzSBp6e/zkifi6xxbl8SmQ/CxQq32k8NNqrdVEVUVSEf56L4rQ/ZxA==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "chokidar": "^2.1.8"
+ },
+ "dependencies": {
+ "anymatch": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "micromatch": "^3.1.4",
+ "normalize-path": "^2.1.1"
+ },
+ "dependencies": {
+ "normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "remove-trailing-separator": "^1.0.1"
+ }
+ }
+ }
+ },
+ "binary-extensions": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+ "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
+ "dev": true,
+ "optional": true
+ },
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "chokidar": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+ "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "anymatch": "^2.0.0",
+ "async-each": "^1.0.1",
+ "braces": "^2.3.2",
+ "fsevents": "^1.2.7",
+ "glob-parent": "^3.1.0",
+ "inherits": "^2.0.3",
+ "is-binary-path": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "normalize-path": "^3.0.0",
+ "path-is-absolute": "^1.0.0",
+ "readdirp": "^2.2.1",
+ "upath": "^1.1.1"
+ }
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "fsevents": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+ "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "bindings": "^1.5.0",
+ "nan": "^2.12.1"
+ }
+ },
+ "glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
+ },
+ "dependencies": {
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "is-extglob": "^2.1.0"
+ }
+ }
+ }
+ },
+ "is-binary-path": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "binary-extensions": "^1.0.0"
+ }
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ }
+ },
+ "readdirp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "micromatch": "^3.1.10",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ }
+ }
+ },
+ "wbuf": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz",
+ "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==",
+ "dev": true,
+ "requires": {
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "wcwidth": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+ "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=",
+ "dev": true,
+ "requires": {
+ "defaults": "^1.0.3"
+ }
+ },
+ "webdriver-js-extender": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz",
+ "integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==",
+ "dev": true,
+ "requires": {
+ "@types/selenium-webdriver": "^3.0.0",
+ "selenium-webdriver": "^3.0.1"
+ }
+ },
+ "webidl-conversions": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz",
+ "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==",
+ "dev": true
+ },
+ "webpack": {
+ "version": "4.44.1",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.1.tgz",
+ "integrity": "sha512-4UOGAohv/VGUNQJstzEywwNxqX417FnjZgZJpJQegddzPmTvph37eBIRbRTfdySXzVtJXLJfbMN3mMYhM6GdmQ==",
+ "dev": true,
+ "requires": {
+ "@webassemblyjs/ast": "1.9.0",
+ "@webassemblyjs/helper-module-context": "1.9.0",
+ "@webassemblyjs/wasm-edit": "1.9.0",
+ "@webassemblyjs/wasm-parser": "1.9.0",
+ "acorn": "^6.4.1",
+ "ajv": "^6.10.2",
+ "ajv-keywords": "^3.4.1",
+ "chrome-trace-event": "^1.0.2",
+ "enhanced-resolve": "^4.3.0",
+ "eslint-scope": "^4.0.3",
+ "json-parse-better-errors": "^1.0.2",
+ "loader-runner": "^2.4.0",
+ "loader-utils": "^1.2.3",
+ "memory-fs": "^0.4.1",
+ "micromatch": "^3.1.10",
+ "mkdirp": "^0.5.3",
+ "neo-async": "^2.6.1",
+ "node-libs-browser": "^2.2.1",
+ "schema-utils": "^1.0.0",
+ "tapable": "^1.1.3",
+ "terser-webpack-plugin": "^1.4.3",
+ "watchpack": "^1.7.4",
+ "webpack-sources": "^1.4.1"
+ },
+ "dependencies": {
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "cacache": {
+ "version": "12.0.4",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
+ "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
+ "dev": true,
+ "requires": {
+ "bluebird": "^3.5.5",
+ "chownr": "^1.1.1",
+ "figgy-pudding": "^3.5.1",
+ "glob": "^7.1.4",
+ "graceful-fs": "^4.1.15",
+ "infer-owner": "^1.0.3",
+ "lru-cache": "^5.1.1",
+ "mississippi": "^3.0.0",
+ "mkdirp": "^0.5.1",
+ "move-concurrently": "^1.0.1",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^2.6.3",
+ "ssri": "^6.0.1",
+ "unique-filename": "^1.1.1",
+ "y18n": "^4.0.0"
+ }
+ },
+ "chownr": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
+ "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
+ "dev": true
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "find-cache-dir": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
+ "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
+ "dev": true,
+ "requires": {
+ "commondir": "^1.0.1",
+ "make-dir": "^2.0.0",
+ "pkg-dir": "^3.0.0"
+ }
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "is-wsl": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+ "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
+ "dev": true
+ },
+ "json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "loader-utils": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
+ "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
+ "dev": true,
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^1.0.1"
+ }
+ },
+ "lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "requires": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "memory-fs": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
+ "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+ "dev": true,
+ "requires": {
+ "errno": "^0.1.3",
+ "readable-stream": "^2.0.1"
+ }
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ }
+ },
+ "rimraf": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+ "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "schema-utils": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+ "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.1.0",
+ "ajv-errors": "^1.0.0",
+ "ajv-keywords": "^3.1.0"
+ }
+ },
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ },
+ "ssri": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
+ "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
+ "dev": true,
+ "requires": {
+ "figgy-pudding": "^3.5.1"
+ }
+ },
+ "terser": {
+ "version": "4.8.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
+ "integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
+ "dev": true,
+ "requires": {
+ "commander": "^2.20.0",
+ "source-map": "~0.6.1",
+ "source-map-support": "~0.5.12"
+ }
+ },
+ "terser-webpack-plugin": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz",
+ "integrity": "sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==",
+ "dev": true,
+ "requires": {
+ "cacache": "^12.0.2",
+ "find-cache-dir": "^2.1.0",
+ "is-wsl": "^1.1.0",
+ "schema-utils": "^1.0.0",
+ "serialize-javascript": "^4.0.0",
+ "source-map": "^0.6.1",
+ "terser": "^4.1.2",
+ "webpack-sources": "^1.4.0",
+ "worker-farm": "^1.7.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ },
+ "yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true
+ }
+ }
+ },
+ "webpack-dev-middleware": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz",
+ "integrity": "sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw==",
+ "dev": true,
+ "requires": {
+ "memory-fs": "^0.4.1",
+ "mime": "^2.4.4",
+ "mkdirp": "^0.5.1",
+ "range-parser": "^1.2.1",
+ "webpack-log": "^2.0.0"
+ },
+ "dependencies": {
+ "memory-fs": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
+ "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+ "dev": true,
+ "requires": {
+ "errno": "^0.1.3",
+ "readable-stream": "^2.0.1"
+ }
+ },
+ "mime": {
+ "version": "2.4.6",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
+ "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==",
+ "dev": true
+ }
+ }
+ },
+ "webpack-dev-server": {
+ "version": "3.11.0",
+ "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz",
+ "integrity": "sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg==",
+ "dev": true,
+ "requires": {
+ "ansi-html": "0.0.7",
+ "bonjour": "^3.5.0",
+ "chokidar": "^2.1.8",
+ "compression": "^1.7.4",
+ "connect-history-api-fallback": "^1.6.0",
+ "debug": "^4.1.1",
+ "del": "^4.1.1",
+ "express": "^4.17.1",
+ "html-entities": "^1.3.1",
+ "http-proxy-middleware": "0.19.1",
+ "import-local": "^2.0.0",
+ "internal-ip": "^4.3.0",
+ "ip": "^1.1.5",
+ "is-absolute-url": "^3.0.3",
+ "killable": "^1.0.1",
+ "loglevel": "^1.6.8",
+ "opn": "^5.5.0",
+ "p-retry": "^3.0.1",
+ "portfinder": "^1.0.26",
+ "schema-utils": "^1.0.0",
+ "selfsigned": "^1.10.7",
+ "semver": "^6.3.0",
+ "serve-index": "^1.9.1",
+ "sockjs": "0.3.20",
+ "sockjs-client": "1.4.0",
+ "spdy": "^4.0.2",
+ "strip-ansi": "^3.0.1",
+ "supports-color": "^6.1.0",
+ "url": "^0.11.0",
+ "webpack-dev-middleware": "^3.7.2",
+ "webpack-log": "^2.0.0",
+ "ws": "^6.2.1",
+ "yargs": "^13.3.2"
+ },
+ "dependencies": {
+ "anymatch": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "dev": true,
+ "requires": {
+ "micromatch": "^3.1.4",
+ "normalize-path": "^2.1.1"
+ },
+ "dependencies": {
+ "normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+ "dev": true,
+ "requires": {
+ "remove-trailing-separator": "^1.0.1"
+ }
+ }
+ }
+ },
+ "binary-extensions": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+ "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
+ "dev": true
+ },
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "chokidar": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+ "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+ "dev": true,
+ "requires": {
+ "anymatch": "^2.0.0",
+ "async-each": "^1.0.1",
+ "braces": "^2.3.2",
+ "fsevents": "^1.2.7",
+ "glob-parent": "^3.1.0",
+ "inherits": "^2.0.3",
+ "is-binary-path": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "normalize-path": "^3.0.0",
+ "path-is-absolute": "^1.0.0",
+ "readdirp": "^2.2.1",
+ "upath": "^1.1.1"
+ }
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "fsevents": {
+ "version": "1.2.13",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
+ "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "bindings": "^1.5.0",
+ "nan": "^2.12.1"
+ }
+ },
+ "glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+ "dev": true,
+ "requires": {
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
+ },
+ "dependencies": {
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.0"
+ }
+ }
+ }
+ },
+ "is-absolute-url": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-3.0.3.tgz",
+ "integrity": "sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q==",
+ "dev": true
+ },
+ "is-binary-path": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^1.0.0"
+ }
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ }
+ },
+ "readdirp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "micromatch": "^3.1.10",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "schema-utils": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+ "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.1.0",
+ "ajv-errors": "^1.0.0",
+ "ajv-keywords": "^3.1.0"
+ }
+ },
+ "semver": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+ "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ }
+ }
+ },
+ "webpack-log": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-2.0.0.tgz",
+ "integrity": "sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "^3.0.0",
+ "uuid": "^3.3.2"
+ },
+ "dependencies": {
+ "uuid": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
+ "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+ "dev": true
+ }
+ }
+ },
+ "webpack-merge": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-4.2.2.tgz",
+ "integrity": "sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g==",
+ "dev": true,
+ "requires": {
+ "lodash": "^4.17.15"
+ }
+ },
+ "webpack-sources": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
+ "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
+ "dev": true,
+ "requires": {
+ "source-list-map": "^2.0.0",
+ "source-map": "~0.6.1"
+ },
+ "dependencies": {
+ "source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
+ }
+ }
+ },
+ "webpack-subresource-integrity": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-1.4.1.tgz",
+ "integrity": "sha512-XMLFInbGbB1HV7K4vHWANzc1CN0t/c4bBvnlvGxGwV45yE/S/feAXIm8dJsCkzqWtSKnmaEgTp/meyeThxG4Iw==",
+ "dev": true,
+ "requires": {
+ "webpack-sources": "^1.3.0"
+ }
+ },
+ "websocket-driver": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz",
+ "integrity": "sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY=",
+ "dev": true,
+ "requires": {
+ "websocket-extensions": ">=0.1.1"
+ }
+ },
+ "websocket-extensions": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+ "dev": true
+ },
+ "whatwg-mimetype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
+ "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==",
+ "dev": true
+ },
+ "whatwg-url": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz",
+ "integrity": "sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==",
+ "dev": true,
+ "requires": {
+ "lodash.sortby": "^4.7.0",
+ "tr46": "^2.0.2",
+ "webidl-conversions": "^6.1.0"
+ }
+ },
+ "when": {
+ "version": "3.6.4",
+ "resolved": "https://registry.npmjs.org/when/-/when-3.6.4.tgz",
+ "integrity": "sha1-RztRfsFZ4rhQBUl6E5g/CVQS404=",
+ "dev": true
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "which-module": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
+ },
+ "worker-farm": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
+ "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
+ "dev": true,
+ "requires": {
+ "errno": "~0.1.7"
+ }
+ },
+ "worker-plugin": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/worker-plugin/-/worker-plugin-5.0.0.tgz",
+ "integrity": "sha512-AXMUstURCxDD6yGam2r4E34aJg6kW85IiaeX72hi+I1cxyaMUtrvVY6sbfpGKAj5e7f68Acl62BjQF5aOOx2IQ==",
+ "dev": true,
+ "requires": {
+ "loader-utils": "^1.1.0"
+ },
+ "dependencies": {
+ "json5": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+ "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "dev": true,
+ "requires": {
+ "minimist": "^1.2.0"
+ }
+ },
+ "loader-utils": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
+ "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
+ "dev": true,
+ "requires": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^1.0.1"
+ }
+ }
+ }
+ },
+ "wrap-ansi": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+ "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "string-width": "^3.0.0",
+ "strip-ansi": "^5.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+ "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+ "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.1.0"
+ }
+ }
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ },
+ "ws": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
+ "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
+ "dev": true,
+ "requires": {
+ "async-limiter": "~1.0.0"
+ }
+ },
+ "xml2js": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
+ "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
+ "dev": true,
+ "requires": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~11.0.0"
+ }
+ },
+ "xmlbuilder": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
+ "dev": true
+ },
+ "xmlhttprequest-ssl": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz",
+ "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=",
+ "dev": true
+ },
+ "xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "dev": true
+ },
+ "y18n": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+ "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
+ },
+ "yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "13.3.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+ "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.2"
+ }
+ },
+ "yargs-parser": {
+ "version": "13.1.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+ "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ },
+ "dependencies": {
+ "camelcase": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+ "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+ "dev": true
+ }
+ }
+ },
+ "yeast": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
+ "integrity": "sha1-AI4G2AlDIMNy28L47XagymyKxBk=",
+ "dev": true
+ },
+ "yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "dev": true
+ },
+ "zone.js": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.10.3.tgz",
+ "integrity": "sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg=="
+ }
+ }
+}
diff --git a/src-ui/package.json b/src-ui/package.json
new file mode 100644
index 000000000..6293f2672
--- /dev/null
+++ b/src-ui/package.json
@@ -0,0 +1,55 @@
+{
+ "name": "paperless-ui",
+ "version": "0.0.0",
+ "scripts": {
+ "ng": "ng",
+ "start": "ng serve",
+ "build": "ng build",
+ "test": "ng test",
+ "lint": "ng lint",
+ "e2e": "ng e2e"
+ },
+ "private": true,
+ "dependencies": {
+ "@angular/animations": "~10.1.5",
+ "@angular/common": "~10.1.5",
+ "@angular/compiler": "~10.1.5",
+ "@angular/core": "~10.1.5",
+ "@angular/forms": "~10.1.5",
+ "@angular/localize": "~10.1.5",
+ "@angular/platform-browser": "~10.1.5",
+ "@angular/platform-browser-dynamic": "~10.1.5",
+ "@angular/router": "~10.1.5",
+ "@ng-bootstrap/ng-bootstrap": "^8.0.0",
+ "bootstrap": "^4.5.0",
+ "ng-bootstrap": "^1.6.3",
+ "ng2-pdf-viewer": "^6.3.2",
+ "ngx-cookie-service": "^10.1.1",
+ "ngx-file-drop": "^10.0.0",
+ "ngx-infinite-scroll": "^9.1.0",
+ "rxjs": "~6.6.0",
+ "tslib": "^2.0.0",
+ "uuid": "^8.3.1",
+ "zone.js": "~0.10.2"
+ },
+ "devDependencies": {
+ "@angular-devkit/build-angular": "^0.1002.0",
+ "@angular/cli": "~10.1.5",
+ "@angular/compiler-cli": "~10.1.5",
+ "@types/jasmine": "~3.5.0",
+ "@types/jasminewd2": "~2.0.3",
+ "@types/node": "^12.11.1",
+ "codelyzer": "^6.0.0",
+ "jasmine-core": "~3.6.0",
+ "jasmine-spec-reporter": "~5.0.0",
+ "karma": "~5.0.0",
+ "karma-chrome-launcher": "~3.1.0",
+ "karma-coverage-istanbul-reporter": "~3.0.2",
+ "karma-jasmine": "~4.0.0",
+ "karma-jasmine-html-reporter": "^1.5.0",
+ "protractor": "~7.0.0",
+ "ts-node": "~8.3.0",
+ "tslint": "~6.1.0",
+ "typescript": "~4.0.2"
+ }
+}
diff --git a/src-ui/src/app/app-routing.module.ts b/src-ui/src/app/app-routing.module.ts
new file mode 100644
index 000000000..27f0629b4
--- /dev/null
+++ b/src-ui/src/app/app-routing.module.ts
@@ -0,0 +1,39 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+import { AppFrameComponent } from './components/app-frame/app-frame.component';
+import { DashboardComponent } from './components/dashboard/dashboard.component';
+import { DocumentDetailComponent } from './components/document-detail/document-detail.component';
+import { DocumentListComponent } from './components/document-list/document-list.component';
+import { CorrespondentListComponent } from './components/manage/correspondent-list/correspondent-list.component';
+import { DocumentTypeListComponent } from './components/manage/document-type-list/document-type-list.component';
+import { LogsComponent } from './components/manage/logs/logs.component';
+import { SettingsComponent } from './components/manage/settings/settings.component';
+import { TagListComponent } from './components/manage/tag-list/tag-list.component';
+import { NotFoundComponent } from './components/not-found/not-found.component';
+import { SearchComponent } from './components/search/search.component';
+
+const routes: Routes = [
+ {path: '', redirectTo: 'dashboard', pathMatch: 'full'},
+ {path: '', component: AppFrameComponent, children: [
+ {path: 'dashboard', component: DashboardComponent },
+ {path: 'documents', component: DocumentListComponent },
+ {path: 'view/:id', component: DocumentListComponent },
+ {path: 'search', component: SearchComponent },
+ {path: 'documents/:id', component: DocumentDetailComponent },
+
+ {path: 'tags', component: TagListComponent },
+ {path: 'documenttypes', component: DocumentTypeListComponent },
+ {path: 'correspondents', component: CorrespondentListComponent },
+ {path: 'logs', component: LogsComponent },
+ {path: 'settings', component: SettingsComponent },
+ ]},
+
+ {path: '404', component: NotFoundComponent},
+ {path: '**', redirectTo: '/404', pathMatch: 'full'}
+];
+
+@NgModule({
+ imports: [RouterModule.forRoot(routes)],
+ exports: [RouterModule]
+})
+export class AppRoutingModule { }
diff --git a/src-ui/src/app/app.component.html b/src-ui/src/app/app.component.html
new file mode 100644
index 000000000..bcf2bba31
--- /dev/null
+++ b/src-ui/src/app/app.component.html
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/data/.keep b/src-ui/src/app/app.component.scss
similarity index 100%
rename from data/.keep
rename to src-ui/src/app/app.component.scss
diff --git a/src-ui/src/app/app.component.spec.ts b/src-ui/src/app/app.component.spec.ts
new file mode 100644
index 000000000..969a50dd9
--- /dev/null
+++ b/src-ui/src/app/app.component.spec.ts
@@ -0,0 +1,35 @@
+import { TestBed } from '@angular/core/testing';
+import { RouterTestingModule } from '@angular/router/testing';
+import { AppComponent } from './app.component';
+
+describe('AppComponent', () => {
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [
+ RouterTestingModule
+ ],
+ declarations: [
+ AppComponent
+ ],
+ }).compileComponents();
+ });
+
+ it('should create the app', () => {
+ const fixture = TestBed.createComponent(AppComponent);
+ const app = fixture.componentInstance;
+ expect(app).toBeTruthy();
+ });
+
+ it(`should have as title 'paperless-ui'`, () => {
+ const fixture = TestBed.createComponent(AppComponent);
+ const app = fixture.componentInstance;
+ expect(app.title).toEqual('paperless-ui');
+ });
+
+ it('should render title', () => {
+ const fixture = TestBed.createComponent(AppComponent);
+ fixture.detectChanges();
+ const compiled = fixture.nativeElement;
+ expect(compiled.querySelector('.content span').textContent).toContain('paperless-ui app is running!');
+ });
+});
diff --git a/src-ui/src/app/app.component.ts b/src-ui/src/app/app.component.ts
new file mode 100644
index 000000000..84c173a18
--- /dev/null
+++ b/src-ui/src/app/app.component.ts
@@ -0,0 +1,14 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-root',
+ templateUrl: './app.component.html',
+ styleUrls: ['./app.component.scss']
+})
+export class AppComponent {
+
+ constructor () {
+ }
+
+
+}
diff --git a/src-ui/src/app/app.module.ts b/src-ui/src/app/app.module.ts
new file mode 100644
index 000000000..3c00cd0b7
--- /dev/null
+++ b/src-ui/src/app/app.module.ts
@@ -0,0 +1,127 @@
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule } from '@angular/core';
+
+import { AppRoutingModule } from './app-routing.module';
+import { AppComponent } from './app.component';
+import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
+import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
+import { DocumentListComponent } from './components/document-list/document-list.component';
+import { DocumentDetailComponent } from './components/document-detail/document-detail.component';
+import { DashboardComponent } from './components/dashboard/dashboard.component';
+import { TagListComponent } from './components/manage/tag-list/tag-list.component';
+import { DocumentTypeListComponent } from './components/manage/document-type-list/document-type-list.component';
+import { LogsComponent } from './components/manage/logs/logs.component';
+import { SettingsComponent } from './components/manage/settings/settings.component';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { DatePipe } from '@angular/common';
+import { NotFoundComponent } from './components/not-found/not-found.component';
+import { CorrespondentListComponent } from './components/manage/correspondent-list/correspondent-list.component';
+import { ConfirmDialogComponent } from './components/common/confirm-dialog/confirm-dialog.component';
+import { CorrespondentEditDialogComponent } from './components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component';
+import { TagEditDialogComponent } from './components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component';
+import { DocumentTypeEditDialogComponent } from './components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component';
+import { TagComponent } from './components/common/tag/tag.component';
+import { SearchComponent } from './components/search/search.component';
+import { ResultHighlightComponent } from './components/search/result-highlight/result-highlight.component';
+import { PageHeaderComponent } from './components/common/page-header/page-header.component';
+import { AppFrameComponent } from './components/app-frame/app-frame.component';
+import { ToastsComponent } from './components/common/toasts/toasts.component';
+import { FilterEditorComponent } from './components/filter-editor/filter-editor.component';
+import { FilterDropdownComponent } from './components/filter-editor/filter-dropdown/filter-dropdown.component';
+import { FilterDropdownButtonComponent } from './components/filter-editor/filter-dropdown/filter-dropdown-button/filter-dropdown-button.component';
+import { FilterDropdownDateComponent } from './components/filter-editor/filter-dropdown-date/filter-dropdown-date.component';
+import { DocumentCardLargeComponent } from './components/document-list/document-card-large/document-card-large.component';
+import { DocumentCardSmallComponent } from './components/document-list/document-card-small/document-card-small.component';
+import { NgxFileDropModule } from 'ngx-file-drop';
+import { TextComponent } from './components/common/input/text/text.component';
+import { SelectComponent } from './components/common/input/select/select.component';
+import { CheckComponent } from './components/common/input/check/check.component';
+import { SaveViewConfigDialogComponent } from './components/document-list/save-view-config-dialog/save-view-config-dialog.component';
+import { InfiniteScrollModule } from 'ngx-infinite-scroll';
+import { DateTimeComponent } from './components/common/input/date-time/date-time.component';
+import { TagsComponent } from './components/common/input/tags/tags.component';
+import { SortableDirective } from './directives/sortable.directive';
+import { CookieService } from 'ngx-cookie-service';
+import { CsrfInterceptor } from './interceptors/csrf.interceptor';
+import { SavedViewWidgetComponent } from './components/dashboard/widgets/saved-view-widget/saved-view-widget.component';
+import { StatisticsWidgetComponent } from './components/dashboard/widgets/statistics-widget/statistics-widget.component';
+import { UploadFileWidgetComponent } from './components/dashboard/widgets/upload-file-widget/upload-file-widget.component';
+import { WidgetFrameComponent } from './components/dashboard/widgets/widget-frame/widget-frame.component';
+import { PdfViewerModule } from 'ng2-pdf-viewer';
+import { WelcomeWidgetComponent } from './components/dashboard/widgets/welcome-widget/welcome-widget.component';
+import { YesNoPipe } from './pipes/yes-no.pipe';
+import { FileSizePipe } from './pipes/file-size.pipe';
+import { FilterPipe } from './pipes/filter.pipe';
+import { DocumentTitlePipe } from './pipes/document-title.pipe';
+import { MetadataCollapseComponent } from './components/document-detail/metadata-collapse/metadata-collapse.component';
+
+@NgModule({
+ declarations: [
+ AppComponent,
+ DocumentListComponent,
+ DocumentDetailComponent,
+ DashboardComponent,
+ TagListComponent,
+ CorrespondentListComponent,
+ DocumentTypeListComponent,
+ LogsComponent,
+ SettingsComponent,
+ NotFoundComponent,
+ CorrespondentEditDialogComponent,
+ ConfirmDialogComponent,
+ TagEditDialogComponent,
+ DocumentTypeEditDialogComponent,
+ TagComponent,
+ SearchComponent,
+ ResultHighlightComponent,
+ PageHeaderComponent,
+ AppFrameComponent,
+ ToastsComponent,
+ FilterEditorComponent,
+ FilterDropdownComponent,
+ FilterDropdownButtonComponent,
+ FilterDropdownDateComponent,
+ DocumentCardLargeComponent,
+ DocumentCardSmallComponent,
+ TextComponent,
+ SelectComponent,
+ CheckComponent,
+ SaveViewConfigDialogComponent,
+ DateTimeComponent,
+ TagsComponent,
+ SortableDirective,
+ SavedViewWidgetComponent,
+ StatisticsWidgetComponent,
+ UploadFileWidgetComponent,
+ WidgetFrameComponent,
+ WelcomeWidgetComponent,
+ YesNoPipe,
+ FileSizePipe,
+ FilterPipe,
+ DocumentTitlePipe,
+ MetadataCollapseComponent
+ ],
+ imports: [
+ BrowserModule,
+ AppRoutingModule,
+ NgbModule,
+ HttpClientModule,
+ FormsModule,
+ ReactiveFormsModule,
+ NgxFileDropModule,
+ InfiniteScrollModule,
+ PdfViewerModule
+ ],
+ providers: [
+ DatePipe,
+ CookieService, {
+ provide: HTTP_INTERCEPTORS,
+ useClass: CsrfInterceptor,
+ multi: true
+ },
+ FilterPipe,
+ DocumentTitlePipe
+ ],
+ bootstrap: [AppComponent]
+})
+export class AppModule { }
diff --git a/src-ui/src/app/components/app-frame/app-frame.component.html b/src-ui/src/app/components/app-frame/app-frame.component.html
new file mode 100644
index 000000000..2458005f4
--- /dev/null
+++ b/src-ui/src/app/components/app-frame/app-frame.component.html
@@ -0,0 +1,171 @@
+
+
+
+ Paperless-ng
+
+
+
+
+
+
+
+
diff --git a/src-ui/src/app/components/app-frame/app-frame.component.scss b/src-ui/src/app/components/app-frame/app-frame.component.scss
new file mode 100644
index 000000000..5ace8a2ff
--- /dev/null
+++ b/src-ui/src/app/components/app-frame/app-frame.component.scss
@@ -0,0 +1,88 @@
+
+@import "/src/theme";
+
+ /*
+ * Sidebar
+ */
+
+ .sidebar {
+ position: fixed;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 100; /* Behind the navbar */
+ padding: 48px 0 0; /* Height of navbar */
+ box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
+}
+
+@media (max-width: 767.98px) {
+ .sidebar {
+ top: 3rem;
+ }
+}
+
+.sidebar-sticky {
+ position: relative;
+ top: 0;
+ /* height: calc(100vh - 48px); */
+ height: 100%;
+ padding-top: .5rem;
+ overflow-x: hidden;
+ overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
+}
+
+@supports ((position: -webkit-sticky) or (position: sticky)) {
+ .sidebar-sticky {
+ position: -webkit-sticky;
+ position: sticky;
+ }
+}
+
+.sidebar .nav-link {
+ font-weight: 500;
+ color: #333;
+}
+
+.sidebar .nav-link .sidebaricon {
+ margin-right: 4px;
+ color: #999;
+}
+
+.sidebar .nav-link.active {
+ color: $primary;
+ font-weight: bold;
+}
+
+.sidebar .nav-link:hover .sidebaricon,
+.sidebar .nav-link.active .sidebaricon {
+ color: inherit;
+}
+
+.sidebar-heading {
+ font-size: .75rem;
+ text-transform: uppercase;
+}
+
+
+/*
+ * Navbar
+ */
+
+ .navbar-brand {
+ padding-top: .75rem;
+ padding-bottom: .75rem;
+ font-size: 1rem;
+ background-color: rgba(0, 0, 0, .25);
+ box-shadow: inset -1px 0 0 rgba(0, 0, 0, .25);
+}
+
+.navbar .navbar-toggler {
+ top: .25rem;
+ right: 1rem;
+}
+
+.navbar .form-control {
+ padding: .75rem 1rem;
+ border-width: 0;
+ border-radius: 0;
+}
diff --git a/src-ui/src/app/components/app-frame/app-frame.component.spec.ts b/src-ui/src/app/components/app-frame/app-frame.component.spec.ts
new file mode 100644
index 000000000..2d0df437c
--- /dev/null
+++ b/src-ui/src/app/components/app-frame/app-frame.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { AppFrameComponent } from './app-frame.component';
+
+describe('AppFrameComponent', () => {
+ let component: AppFrameComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ AppFrameComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(AppFrameComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/app-frame/app-frame.component.ts b/src-ui/src/app/components/app-frame/app-frame.component.ts
new file mode 100644
index 000000000..c4c00843d
--- /dev/null
+++ b/src-ui/src/app/components/app-frame/app-frame.component.ts
@@ -0,0 +1,101 @@
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import { FormControl } from '@angular/forms';
+import { ActivatedRoute, Router } from '@angular/router';
+import { from, Observable, Subscription } from 'rxjs';
+import { debounceTime, distinctUntilChanged, map, switchMap } from 'rxjs/operators';
+import { PaperlessDocument } from 'src/app/data/paperless-document';
+import { OpenDocumentsService } from 'src/app/services/open-documents.service';
+import { SavedViewService } from 'src/app/services/rest/saved-view.service';
+import { SearchService } from 'src/app/services/rest/search.service';
+import { environment } from 'src/environments/environment';
+import { DocumentDetailComponent } from '../document-detail/document-detail.component';
+
+@Component({
+ selector: 'app-app-frame',
+ templateUrl: './app-frame.component.html',
+ styleUrls: ['./app-frame.component.scss']
+})
+export class AppFrameComponent implements OnInit, OnDestroy {
+
+ constructor (
+ public router: Router,
+ private activatedRoute: ActivatedRoute,
+ private openDocumentsService: OpenDocumentsService,
+ private searchService: SearchService,
+ public savedViewService: SavedViewService
+ ) {
+ }
+
+ versionString = `${environment.appTitle} ${environment.version}`
+
+ isMenuCollapsed: boolean = true
+
+ closeMenu() {
+ this.isMenuCollapsed = true
+ }
+
+ searchField = new FormControl('')
+
+ openDocuments: PaperlessDocument[] = []
+
+ openDocumentsSubscription: Subscription
+
+ searchAutoComplete = (text$: Observable) =>
+ text$.pipe(
+ debounceTime(200),
+ distinctUntilChanged(),
+ map(term => {
+ if (term.lastIndexOf(' ') != -1) {
+ return term.substring(term.lastIndexOf(' ') + 1)
+ } else {
+ return term
+ }
+ }),
+ switchMap(term =>
+ term.length < 2 ? from([[]]) : this.searchService.autocomplete(term)
+ )
+ )
+
+ itemSelected(event) {
+ event.preventDefault()
+ let currentSearch: string = this.searchField.value
+ let lastSpaceIndex = currentSearch.lastIndexOf(' ')
+ if (lastSpaceIndex != -1) {
+ currentSearch = currentSearch.substring(0, lastSpaceIndex + 1)
+ currentSearch += event.item + " "
+ } else {
+ currentSearch = event.item + " "
+ }
+ this.searchField.patchValue(currentSearch)
+ }
+
+ search() {
+ this.closeMenu()
+ this.router.navigate(['search'], {queryParams: {query: this.searchField.value}})
+ }
+
+ closeAll() {
+ this.closeMenu()
+ this.openDocumentsService.closeAll()
+
+ // TODO: is there a better way to do this?
+ let route = this.activatedRoute
+ while (route.firstChild) {
+ route = route.firstChild
+ }
+ if (route.component == DocumentDetailComponent) {
+ this.router.navigate([""])
+ }
+ }
+
+ ngOnInit() {
+ this.openDocuments = this.openDocumentsService.getOpenDocuments()
+ }
+
+ ngOnDestroy() {
+ if (this.openDocumentsSubscription) {
+ this.openDocumentsSubscription.unsubscribe()
+ }
+ }
+
+}
diff --git a/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.html b/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.html
new file mode 100644
index 000000000..53b613244
--- /dev/null
+++ b/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.html
@@ -0,0 +1,14 @@
+
+
+
{{messageBold}}
+
{{message}}
+
+
\ No newline at end of file
diff --git a/docs/requirements.txt b/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.scss
similarity index 100%
rename from docs/requirements.txt
rename to src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.scss
diff --git a/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.spec.ts b/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.spec.ts
new file mode 100644
index 000000000..fe08dc57a
--- /dev/null
+++ b/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ConfirmDialogComponent } from './confirm-dialog.component';
+
+describe('ConfirmDialogComponent', () => {
+ let component: ConfirmDialogComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ ConfirmDialogComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ConfirmDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.ts b/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.ts
new file mode 100644
index 000000000..e207f4598
--- /dev/null
+++ b/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.ts
@@ -0,0 +1,37 @@
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+
+@Component({
+ selector: 'app-confirm-dialog',
+ templateUrl: './confirm-dialog.component.html',
+ styleUrls: ['./confirm-dialog.component.scss']
+})
+export class ConfirmDialogComponent implements OnInit {
+
+ constructor(public activeModal: NgbActiveModal) { }
+
+ @Output()
+ public confirmClicked = new EventEmitter()
+
+ @Input()
+ title = "Confirmation"
+
+ @Input()
+ messageBold
+
+ @Input()
+ message
+
+ @Input()
+ btnClass = "btn-primary"
+
+ @Input()
+ btnCaption = "Confirm"
+
+ ngOnInit(): void {
+ }
+
+ cancelClicked() {
+ this.activeModal.close()
+ }
+}
diff --git a/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.spec.ts
new file mode 100644
index 000000000..597599604
--- /dev/null
+++ b/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { EditDialogComponent } from './edit-dialog.component';
+
+describe('EditDialogComponent', () => {
+ let component: EditDialogComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ EditDialogComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(EditDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.ts
new file mode 100644
index 000000000..ba0d90847
--- /dev/null
+++ b/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.ts
@@ -0,0 +1,76 @@
+import { Directive, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { FormGroup } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { Observable } from 'rxjs';
+import { MATCHING_ALGORITHMS } from 'src/app/data/matching-model';
+import { ObjectWithId } from 'src/app/data/object-with-id';
+import { AbstractPaperlessService } from 'src/app/services/rest/abstract-paperless-service';
+import { Toast, ToastService } from 'src/app/services/toast.service';
+
+@Directive()
+export abstract class EditDialogComponent implements OnInit {
+
+ constructor(
+ private service: AbstractPaperlessService,
+ private activeModal: NgbActiveModal,
+ private toastService: ToastService,
+ private entityName: string) { }
+
+ @Input()
+ dialogMode: string = 'create'
+
+ @Input()
+ object: T
+
+ @Output()
+ success = new EventEmitter()
+
+ abstract getForm(): FormGroup
+
+ objectForm: FormGroup = this.getForm()
+
+ ngOnInit(): void {
+ if (this.object != null) {
+ this.objectForm.patchValue(this.object)
+ }
+ }
+
+ getTitle() {
+ switch (this.dialogMode) {
+ case 'create':
+ return "Create new " + this.entityName
+ case 'edit':
+ return "Edit " + this.entityName
+ default:
+ break;
+ }
+ }
+
+ getMatchingAlgorithms() {
+ return MATCHING_ALGORITHMS
+ }
+
+ save() {
+ var newObject = Object.assign(Object.assign({}, this.object), this.objectForm.value)
+ var serverResponse: Observable
+ switch (this.dialogMode) {
+ case 'create':
+ serverResponse = this.service.create(newObject)
+ break;
+ case 'edit':
+ serverResponse = this.service.update(newObject)
+ default:
+ break;
+ }
+ serverResponse.subscribe(result => {
+ this.activeModal.close()
+ this.success.emit(result)
+ }, error => {
+ this.toastService.showToast(Toast.makeError(`Could not save ${this.entityName}: ${error.error.name}`))
+ })
+ }
+
+ cancel() {
+ this.activeModal.close()
+ }
+}
diff --git a/src-ui/src/app/components/common/input/abstract-input.ts b/src-ui/src/app/components/common/input/abstract-input.ts
new file mode 100644
index 000000000..78a4a1b69
--- /dev/null
+++ b/src-ui/src/app/components/common/input/abstract-input.ts
@@ -0,0 +1,43 @@
+import { Directive, Input, OnInit } from '@angular/core';
+import { ControlValueAccessor } from '@angular/forms';
+import { v4 as uuidv4 } from 'uuid';
+
+@Directive()
+export class AbstractInputComponent implements OnInit, ControlValueAccessor {
+
+ constructor() { }
+
+ onChange = (newValue: T) => {};
+
+ onTouched = () => {};
+
+ writeValue(newValue: any): void {
+ this.value = newValue
+ }
+ registerOnChange(fn: any): void {
+ this.onChange = fn;
+ }
+ registerOnTouched(fn: any): void {
+ this.onTouched = fn;
+ }
+ setDisabledState?(isDisabled: boolean): void {
+ this.disabled = isDisabled;
+ }
+
+ @Input()
+ title: string
+
+ @Input()
+ disabled = false;
+
+ value: T
+
+ ngOnInit(): void {
+ this.inputId = uuidv4()
+ }
+
+ inputId: string
+
+ @Input()
+ hint: string
+}
diff --git a/src-ui/src/app/components/common/input/check/check.component.html b/src-ui/src/app/components/common/input/check/check.component.html
new file mode 100644
index 000000000..88bde649e
--- /dev/null
+++ b/src-ui/src/app/components/common/input/check/check.component.html
@@ -0,0 +1,5 @@
+
+
+ {{title}}
+ {{hint}}
+
\ No newline at end of file
diff --git a/media/documents/originals/.keep b/src-ui/src/app/components/common/input/check/check.component.scss
similarity index 100%
rename from media/documents/originals/.keep
rename to src-ui/src/app/components/common/input/check/check.component.scss
diff --git a/src-ui/src/app/components/common/input/check/check.component.spec.ts b/src-ui/src/app/components/common/input/check/check.component.spec.ts
new file mode 100644
index 000000000..9649ed157
--- /dev/null
+++ b/src-ui/src/app/components/common/input/check/check.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { CheckComponent } from './check.component';
+
+describe('CheckComponent', () => {
+ let component: CheckComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ CheckComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CheckComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/common/input/check/check.component.ts b/src-ui/src/app/components/common/input/check/check.component.ts
new file mode 100644
index 000000000..de0b9a0d1
--- /dev/null
+++ b/src-ui/src/app/components/common/input/check/check.component.ts
@@ -0,0 +1,22 @@
+import { Component, forwardRef, Input, OnInit } from '@angular/core';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+import { v4 as uuidv4 } from 'uuid';
+import { AbstractInputComponent } from '../abstract-input';
+
+@Component({
+ providers: [{
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => CheckComponent),
+ multi: true
+ }],
+ selector: 'app-input-check',
+ templateUrl: './check.component.html',
+ styleUrls: ['./check.component.scss']
+})
+export class CheckComponent extends AbstractInputComponent {
+
+ constructor() {
+ super()
+ }
+
+}
diff --git a/src-ui/src/app/components/common/input/date-time/date-time.component.html b/src-ui/src/app/components/common/input/date-time/date-time.component.html
new file mode 100644
index 000000000..7c002db1b
--- /dev/null
+++ b/src-ui/src/app/components/common/input/date-time/date-time.component.html
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/media/documents/thumbnails/.keep b/src-ui/src/app/components/common/input/date-time/date-time.component.scss
similarity index 100%
rename from media/documents/thumbnails/.keep
rename to src-ui/src/app/components/common/input/date-time/date-time.component.scss
diff --git a/src-ui/src/app/components/common/input/date-time/date-time.component.spec.ts b/src-ui/src/app/components/common/input/date-time/date-time.component.spec.ts
new file mode 100644
index 000000000..0657768bd
--- /dev/null
+++ b/src-ui/src/app/components/common/input/date-time/date-time.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DateTimeComponent } from './date-time.component';
+
+describe('DateTimeComponent', () => {
+ let component: DateTimeComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ DateTimeComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DateTimeComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/common/input/date-time/date-time.component.ts b/src-ui/src/app/components/common/input/date-time/date-time.component.ts
new file mode 100644
index 000000000..bce208ec8
--- /dev/null
+++ b/src-ui/src/app/components/common/input/date-time/date-time.component.ts
@@ -0,0 +1,61 @@
+import { formatDate } from '@angular/common';
+import { Component, forwardRef, Input, OnInit } from '@angular/core';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+
+@Component({
+ providers: [{
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => DateTimeComponent),
+ multi: true
+ }],
+ selector: 'app-input-date-time',
+ templateUrl: './date-time.component.html',
+ styleUrls: ['./date-time.component.scss']
+})
+export class DateTimeComponent implements OnInit,ControlValueAccessor {
+
+ constructor() {
+ }
+
+ onChange = (newValue: any) => {};
+
+ onTouched = () => {};
+
+ writeValue(newValue: any): void {
+ this.dateValue = formatDate(newValue, 'yyyy-MM-dd', "en-US")
+ this.timeValue = formatDate(newValue, 'HH:mm:ss', 'en-US')
+ }
+ registerOnChange(fn: any): void {
+ this.onChange = fn;
+ }
+ registerOnTouched(fn: any): void {
+ this.onTouched = fn;
+ }
+ setDisabledState?(isDisabled: boolean): void {
+ this.disabled = isDisabled;
+ }
+
+ @Input()
+ titleDate: string = "Date"
+
+ @Input()
+ titleTime: string
+
+ @Input()
+ disabled: boolean = false
+
+ @Input()
+ hint: string
+
+ timeValue
+
+ dateValue
+
+ ngOnInit(): void {
+ }
+
+ dateOrTimeChanged() {
+ this.onChange(formatDate(this.dateValue + "T" + this.timeValue,"yyyy-MM-ddTHH:mm:ssZZZZZ", "en-us", "UTC"))
+ }
+
+}
diff --git a/src-ui/src/app/components/common/input/select/select.component.html b/src-ui/src/app/components/common/input/select/select.component.html
new file mode 100644
index 000000000..717aa7964
--- /dev/null
+++ b/src-ui/src/app/components/common/input/select/select.component.html
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/src/documents/templatetags/__init__.py b/src-ui/src/app/components/common/input/select/select.component.scss
similarity index 100%
rename from src/documents/templatetags/__init__.py
rename to src-ui/src/app/components/common/input/select/select.component.scss
diff --git a/src-ui/src/app/components/common/input/select/select.component.spec.ts b/src-ui/src/app/components/common/input/select/select.component.spec.ts
new file mode 100644
index 000000000..2fb4207eb
--- /dev/null
+++ b/src-ui/src/app/components/common/input/select/select.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SelectComponent } from './select.component';
+
+describe('SelectComponent', () => {
+ let component: SelectComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ SelectComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(SelectComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/common/input/select/select.component.ts b/src-ui/src/app/components/common/input/select/select.component.ts
new file mode 100644
index 000000000..18f30cf6e
--- /dev/null
+++ b/src-ui/src/app/components/common/input/select/select.component.ts
@@ -0,0 +1,40 @@
+import { Component, EventEmitter, forwardRef, Input, Output } from '@angular/core';
+import { NG_VALUE_ACCESSOR } from '@angular/forms';
+import { AbstractInputComponent } from '../abstract-input';
+
+@Component({
+ providers: [{
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => SelectComponent),
+ multi: true
+ }],
+ selector: 'app-input-select',
+ templateUrl: './select.component.html',
+ styleUrls: ['./select.component.scss']
+})
+export class SelectComponent extends AbstractInputComponent {
+
+ constructor() {
+ super()
+ }
+
+ @Input()
+ items: any[]
+
+ @Input()
+ textColor: any
+
+ @Input()
+ backgroundColor: any
+
+ @Input()
+ allowNull: boolean = false
+
+ @Output()
+ createNew = new EventEmitter()
+
+ showPlusButton(): boolean {
+ return this.createNew.observers.length > 0
+ }
+
+}
diff --git a/src-ui/src/app/components/common/input/tags/tags.component.html b/src-ui/src/app/components/common/input/tags/tags.component.html
new file mode 100644
index 000000000..8029dd860
--- /dev/null
+++ b/src-ui/src/app/components/common/input/tags/tags.component.html
@@ -0,0 +1,30 @@
+
\ No newline at end of file
diff --git a/src-ui/src/app/components/common/input/tags/tags.component.scss b/src-ui/src/app/components/common/input/tags/tags.component.scss
new file mode 100644
index 000000000..f2635b7f2
--- /dev/null
+++ b/src-ui/src/app/components/common/input/tags/tags.component.scss
@@ -0,0 +1,10 @@
+.tags-form-control {
+ height: auto;
+}
+
+
+.scrollable-menu {
+ height: auto;
+ max-height: 300px;
+ overflow-x: hidden;
+}
\ No newline at end of file
diff --git a/src-ui/src/app/components/common/input/tags/tags.component.spec.ts b/src-ui/src/app/components/common/input/tags/tags.component.spec.ts
new file mode 100644
index 000000000..582775da4
--- /dev/null
+++ b/src-ui/src/app/components/common/input/tags/tags.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TagsComponent } from './tags.component';
+
+describe('TagsComponent', () => {
+ let component: TagsComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ TagsComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TagsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/common/input/tags/tags.component.ts b/src-ui/src/app/components/common/input/tags/tags.component.ts
new file mode 100644
index 000000000..cca99cc55
--- /dev/null
+++ b/src-ui/src/app/components/common/input/tags/tags.component.ts
@@ -0,0 +1,94 @@
+import { Component, forwardRef, Input, OnInit } from '@angular/core';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { TagEditDialogComponent } from 'src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component';
+import { PaperlessTag } from 'src/app/data/paperless-tag';
+import { TagService } from 'src/app/services/rest/tag.service';
+
+@Component({
+ providers: [{
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => TagsComponent),
+ multi: true
+ }],
+ selector: 'app-input-tags',
+ templateUrl: './tags.component.html',
+ styleUrls: ['./tags.component.scss']
+})
+export class TagsComponent implements OnInit, ControlValueAccessor {
+
+ constructor(private tagService: TagService, private modalService: NgbModal) { }
+
+
+ onChange = (newValue: number[]) => {};
+
+ onTouched = () => {};
+
+ writeValue(newValue: number[]): void {
+ this.value = newValue
+ if (this.tags) {
+ this.displayValue = newValue
+ }
+ }
+ registerOnChange(fn: any): void {
+ this.onChange = fn;
+ }
+ registerOnTouched(fn: any): void {
+ this.onTouched = fn;
+ }
+ setDisabledState?(isDisabled: boolean): void {
+ this.disabled = isDisabled;
+ }
+
+ ngOnInit(): void {
+ this.tagService.listAll().subscribe(result => {
+ this.tags = result.results
+ this.displayValue = this.value
+ })
+ }
+
+ @Input()
+ disabled = false
+
+ @Input()
+ hint
+
+ value: number[]
+
+ displayValue: number[] = []
+
+ tags: PaperlessTag[]
+
+ getTag(id) {
+ return this.tags.find(tag => tag.id == id)
+ }
+
+ removeTag(id) {
+ let index = this.displayValue.indexOf(id)
+ if (index > -1) {
+ this.displayValue.splice(index, 1)
+ this.onChange(this.displayValue)
+ }
+ }
+
+ addTag(id) {
+ let index = this.displayValue.indexOf(id)
+ if (index == -1) {
+ this.displayValue.push(id)
+ this.onChange(this.displayValue)
+ }
+ }
+
+
+ createTag() {
+ var modal = this.modalService.open(TagEditDialogComponent, {backdrop: 'static'})
+ modal.componentInstance.dialogMode = 'create'
+ modal.componentInstance.success.subscribe(newTag => {
+ this.tagService.listAll().subscribe(tags => {
+ this.tags = tags.results
+ this.addTag(newTag.id)
+ })
+ })
+ }
+
+}
diff --git a/src-ui/src/app/components/common/input/text/text.component.html b/src-ui/src/app/components/common/input/text/text.component.html
new file mode 100644
index 000000000..3a43b052f
--- /dev/null
+++ b/src-ui/src/app/components/common/input/text/text.component.html
@@ -0,0 +1,5 @@
+
+ {{title}}
+
+ {{hint}}
+
\ No newline at end of file
diff --git a/src/reminders/__init__.py b/src-ui/src/app/components/common/input/text/text.component.scss
similarity index 100%
rename from src/reminders/__init__.py
rename to src-ui/src/app/components/common/input/text/text.component.scss
diff --git a/src-ui/src/app/components/common/input/text/text.component.spec.ts b/src-ui/src/app/components/common/input/text/text.component.spec.ts
new file mode 100644
index 000000000..24b1e9e65
--- /dev/null
+++ b/src-ui/src/app/components/common/input/text/text.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TextComponent } from './text.component';
+
+describe('TextComponent', () => {
+ let component: TextComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ TextComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TextComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/common/input/text/text.component.ts b/src-ui/src/app/components/common/input/text/text.component.ts
new file mode 100644
index 000000000..0a1a05749
--- /dev/null
+++ b/src-ui/src/app/components/common/input/text/text.component.ts
@@ -0,0 +1,21 @@
+import { Component, forwardRef } from '@angular/core';
+import { NG_VALUE_ACCESSOR } from '@angular/forms';
+import { AbstractInputComponent } from '../abstract-input';
+
+@Component({
+ providers: [{
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => TextComponent),
+ multi: true
+ }],
+ selector: 'app-input-text',
+ templateUrl: './text.component.html',
+ styleUrls: ['./text.component.scss']
+})
+export class TextComponent extends AbstractInputComponent {
+
+ constructor() {
+ super()
+ }
+
+}
diff --git a/src-ui/src/app/components/common/page-header/page-header.component.html b/src-ui/src/app/components/common/page-header/page-header.component.html
new file mode 100644
index 000000000..fccce9b98
--- /dev/null
+++ b/src-ui/src/app/components/common/page-header/page-header.component.html
@@ -0,0 +1,9 @@
+
+
+
{{title}}
+
{{subTitle}}
+
+
+
+
+
diff --git a/src/reminders/migrations/__init__.py b/src-ui/src/app/components/common/page-header/page-header.component.scss
similarity index 100%
rename from src/reminders/migrations/__init__.py
rename to src-ui/src/app/components/common/page-header/page-header.component.scss
diff --git a/src-ui/src/app/components/common/page-header/page-header.component.spec.ts b/src-ui/src/app/components/common/page-header/page-header.component.spec.ts
new file mode 100644
index 000000000..eb863e4f9
--- /dev/null
+++ b/src-ui/src/app/components/common/page-header/page-header.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PageHeaderComponent } from './page-header.component';
+
+describe('PageHeaderComponent', () => {
+ let component: PageHeaderComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ PageHeaderComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PageHeaderComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/common/page-header/page-header.component.ts b/src-ui/src/app/components/common/page-header/page-header.component.ts
new file mode 100644
index 000000000..153e6bea6
--- /dev/null
+++ b/src-ui/src/app/components/common/page-header/page-header.component.ts
@@ -0,0 +1,29 @@
+import { Component, Input } from '@angular/core';
+import { Title } from '@angular/platform-browser';
+import { environment } from 'src/environments/environment';
+
+@Component({
+ selector: 'app-page-header',
+ templateUrl: './page-header.component.html',
+ styleUrls: ['./page-header.component.scss']
+})
+export class PageHeaderComponent {
+
+ constructor(private titleService: Title) { }
+
+ _title = ""
+
+ @Input()
+ set title(title: string) {
+ this._title = title
+ this.titleService.setTitle(`${this.title} - ${environment.appTitle}`)
+ }
+
+ get title() {
+ return this._title
+ }
+
+ @Input()
+ subTitle: string = ""
+
+}
diff --git a/src-ui/src/app/components/common/tag/tag.component.html b/src-ui/src/app/components/common/tag/tag.component.html
new file mode 100644
index 000000000..8b9632a65
--- /dev/null
+++ b/src-ui/src/app/components/common/tag/tag.component.html
@@ -0,0 +1,2 @@
+{{tag.name}}
+{{tag.name}}
\ No newline at end of file
diff --git a/src-ui/src/app/components/common/tag/tag.component.scss b/src-ui/src/app/components/common/tag/tag.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src-ui/src/app/components/common/tag/tag.component.spec.ts b/src-ui/src/app/components/common/tag/tag.component.spec.ts
new file mode 100644
index 000000000..a2dd3b5a9
--- /dev/null
+++ b/src-ui/src/app/components/common/tag/tag.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TagComponent } from './tag.component';
+
+describe('TagComponent', () => {
+ let component: TagComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ TagComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TagComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/common/tag/tag.component.ts b/src-ui/src/app/components/common/tag/tag.component.ts
new file mode 100644
index 000000000..0b1186ce0
--- /dev/null
+++ b/src-ui/src/app/components/common/tag/tag.component.ts
@@ -0,0 +1,29 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { TAG_COLOURS, PaperlessTag } from 'src/app/data/paperless-tag';
+
+@Component({
+ selector: 'app-tag',
+ templateUrl: './tag.component.html',
+ styleUrls: ['./tag.component.scss']
+})
+export class TagComponent implements OnInit {
+
+ constructor() { }
+
+ @Input()
+ tag: PaperlessTag
+
+ @Input()
+ linkTitle: string = ""
+
+ @Input()
+ clickable: boolean = false
+
+ ngOnInit(): void {
+ }
+
+ getColour() {
+ return TAG_COLOURS.find(c => c.id == this.tag.colour)
+ }
+
+}
diff --git a/src-ui/src/app/components/common/toasts/toasts.component.html b/src-ui/src/app/components/common/toasts/toasts.component.html
new file mode 100644
index 000000000..04aa15a67
--- /dev/null
+++ b/src-ui/src/app/components/common/toasts/toasts.component.html
@@ -0,0 +1,7 @@
+
+ {{toast.content}}
+
\ No newline at end of file
diff --git a/src-ui/src/app/components/common/toasts/toasts.component.scss b/src-ui/src/app/components/common/toasts/toasts.component.scss
new file mode 100644
index 000000000..4d1c95fce
--- /dev/null
+++ b/src-ui/src/app/components/common/toasts/toasts.component.scss
@@ -0,0 +1,7 @@
+:host {
+ position: fixed;
+ top: 0;
+ right: 0;
+ margin: 0.5em;
+ z-index: 1200;
+}
diff --git a/src-ui/src/app/components/common/toasts/toasts.component.spec.ts b/src-ui/src/app/components/common/toasts/toasts.component.spec.ts
new file mode 100644
index 000000000..3486005f1
--- /dev/null
+++ b/src-ui/src/app/components/common/toasts/toasts.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ToastsComponent } from './toasts.component';
+
+describe('ToastsComponent', () => {
+ let component: ToastsComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ ToastsComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ToastsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/common/toasts/toasts.component.ts b/src-ui/src/app/components/common/toasts/toasts.component.ts
new file mode 100644
index 000000000..8f142646d
--- /dev/null
+++ b/src-ui/src/app/components/common/toasts/toasts.component.ts
@@ -0,0 +1,26 @@
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import { Subscription } from 'rxjs';
+import { Toast, ToastService } from 'src/app/services/toast.service';
+
+@Component({
+ selector: 'app-toasts',
+ templateUrl: './toasts.component.html',
+ styleUrls: ['./toasts.component.scss']
+})
+export class ToastsComponent implements OnInit, OnDestroy {
+
+ constructor(private toastService: ToastService) { }
+
+ subscription: Subscription
+
+ toasts: Toast[] = []
+
+ ngOnDestroy(): void {
+ this.subscription.unsubscribe()
+ }
+
+ ngOnInit(): void {
+ this.subscription = this.toastService.getToasts().subscribe(toasts => this.toasts = toasts)
+ }
+
+}
diff --git a/src-ui/src/app/components/dashboard/dashboard.component.html b/src-ui/src/app/components/dashboard/dashboard.component.html
new file mode 100644
index 000000000..627e7ff22
--- /dev/null
+++ b/src-ui/src/app/components/dashboard/dashboard.component.html
@@ -0,0 +1,21 @@
+
+
+
+
+
diff --git a/src-ui/src/app/components/dashboard/dashboard.component.scss b/src-ui/src/app/components/dashboard/dashboard.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src-ui/src/app/components/dashboard/dashboard.component.spec.ts b/src-ui/src/app/components/dashboard/dashboard.component.spec.ts
new file mode 100644
index 000000000..5ec4ff8fc
--- /dev/null
+++ b/src-ui/src/app/components/dashboard/dashboard.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DashboardComponent } from './dashboard.component';
+
+describe('DashboardComponent', () => {
+ let component: DashboardComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ DashboardComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DashboardComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/dashboard/dashboard.component.ts b/src-ui/src/app/components/dashboard/dashboard.component.ts
new file mode 100644
index 000000000..a14ec5e90
--- /dev/null
+++ b/src-ui/src/app/components/dashboard/dashboard.component.ts
@@ -0,0 +1,25 @@
+import { Component, OnInit } from '@angular/core';
+import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
+import { SavedViewService } from 'src/app/services/rest/saved-view.service';
+
+
+@Component({
+ selector: 'app-dashboard',
+ templateUrl: './dashboard.component.html',
+ styleUrls: ['./dashboard.component.scss']
+})
+export class DashboardComponent implements OnInit {
+
+ constructor(
+ private savedViewService: SavedViewService) { }
+
+
+ savedViews: PaperlessSavedView[] = []
+
+ ngOnInit(): void {
+ this.savedViewService.listAll().subscribe(results => {
+ this.savedViews = results.results.filter(savedView => savedView.show_on_dashboard)
+ })
+ }
+
+}
diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html
new file mode 100644
index 000000000..f50708af3
--- /dev/null
+++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html
@@ -0,0 +1,21 @@
+
+
+ Show all
+
+
+
+
+
+ Created
+ Title
+
+
+
+
+ {{doc.created | date}}
+ {{doc.title | documentTitle}}
+
+
+
+
+
\ No newline at end of file
diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.scss b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts
new file mode 100644
index 000000000..f0095b618
--- /dev/null
+++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SavedViewWidgetComponent } from './saved-view-widget.component';
+
+describe('SavedViewWidgetComponent', () => {
+ let component: SavedViewWidgetComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ SavedViewWidgetComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(SavedViewWidgetComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts
new file mode 100644
index 000000000..5bfecc640
--- /dev/null
+++ b/src-ui/src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.ts
@@ -0,0 +1,40 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+import { PaperlessDocument } from 'src/app/data/paperless-document';
+import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
+import { DocumentListViewService } from 'src/app/services/document-list-view.service';
+import { DocumentService } from 'src/app/services/rest/document.service';
+
+@Component({
+ selector: 'app-saved-view-widget',
+ templateUrl: './saved-view-widget.component.html',
+ styleUrls: ['./saved-view-widget.component.scss']
+})
+export class SavedViewWidgetComponent implements OnInit {
+
+ constructor(
+ private documentService: DocumentService,
+ private router: Router,
+ private list: DocumentListViewService) { }
+
+ @Input()
+ savedView: PaperlessSavedView
+
+ documents: PaperlessDocument[] = []
+
+ ngOnInit(): void {
+ this.documentService.list(1,10,this.savedView.sort_field, this.savedView.sort_reverse, this.savedView.filter_rules).subscribe(result => {
+ this.documents = result.results
+ })
+ }
+
+ showAll() {
+ if (this.savedView.show_in_sidebar) {
+ this.router.navigate(['view', this.savedView.id])
+ } else {
+ this.list.load(this.savedView)
+ this.router.navigate(["documents"])
+ }
+ }
+
+}
diff --git a/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html b/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html
new file mode 100644
index 000000000..50d844b36
--- /dev/null
+++ b/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.html
@@ -0,0 +1,6 @@
+
+
+ Documents in inbox: {{statistics.documents_inbox}}
+ Total documents: {{statistics.documents_total}}
+
+
\ No newline at end of file
diff --git a/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.scss b/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.spec.ts b/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.spec.ts
new file mode 100644
index 000000000..e8e44ca54
--- /dev/null
+++ b/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { StatisticsWidgetComponent } from './statistics-widget.component';
+
+describe('StatisticsWidgetComponent', () => {
+ let component: StatisticsWidgetComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ StatisticsWidgetComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(StatisticsWidgetComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.ts b/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.ts
new file mode 100644
index 000000000..73eee698c
--- /dev/null
+++ b/src-ui/src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.ts
@@ -0,0 +1,33 @@
+import { HttpClient } from '@angular/common/http';
+import { Component, OnInit } from '@angular/core';
+import { Observable } from 'rxjs';
+import { environment } from 'src/environments/environment';
+
+export interface Statistics {
+ documents_total?: number
+ documents_inbox?: number
+}
+
+
+@Component({
+ selector: 'app-statistics-widget',
+ templateUrl: './statistics-widget.component.html',
+ styleUrls: ['./statistics-widget.component.scss']
+})
+export class StatisticsWidgetComponent implements OnInit {
+
+ constructor(private http: HttpClient) { }
+
+ statistics: Statistics = {}
+
+ getStatistics(): Observable {
+ return this.http.get(`${environment.apiBaseUrl}statistics/`)
+ }
+
+ ngOnInit(): void {
+ this.getStatistics().subscribe(statistics => {
+ this.statistics = statistics
+ })
+ }
+
+}
diff --git a/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html b/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html
new file mode 100644
index 000000000..013486a47
--- /dev/null
+++ b/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html
@@ -0,0 +1,18 @@
+
+
+
+
+
+
Uploading {{uploadStatus.length}} file(s)
+
0">
+
+
+
+
\ No newline at end of file
diff --git a/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.scss b/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.spec.ts b/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.spec.ts
new file mode 100644
index 000000000..88e8efa2e
--- /dev/null
+++ b/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { UploadFileWidgetComponent } from './upload-file-widget.component';
+
+describe('UploadFileWidgetComponent', () => {
+ let component: UploadFileWidgetComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ UploadFileWidgetComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(UploadFileWidgetComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts b/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts
new file mode 100644
index 000000000..2ea4825f1
--- /dev/null
+++ b/src-ui/src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.ts
@@ -0,0 +1,84 @@
+import { HttpEventType } from '@angular/common/http';
+import { Component, OnInit } from '@angular/core';
+import { FileSystemFileEntry, NgxFileDropEntry } from 'ngx-file-drop';
+import { DocumentService } from 'src/app/services/rest/document.service';
+import { Toast, ToastService } from 'src/app/services/toast.service';
+
+
+interface UploadStatus {
+ loaded: number
+ total: number
+}
+
+@Component({
+ selector: 'app-upload-file-widget',
+ templateUrl: './upload-file-widget.component.html',
+ styleUrls: ['./upload-file-widget.component.scss']
+})
+export class UploadFileWidgetComponent implements OnInit {
+
+ constructor(private documentService: DocumentService, private toastService: ToastService) { }
+
+ ngOnInit(): void {
+ }
+
+ public fileOver(event){
+ }
+
+ public fileLeave(event){
+ }
+
+ uploadStatus: UploadStatus[] = []
+ completedFiles = 0
+
+ uploadVisible = false
+
+ get loadedSum() {
+ return this.uploadStatus.map(s => s.loaded).reduce((a,b) => a+b, this.completedFiles > 0 ? 1 : 0)
+ }
+
+ get totalSum() {
+ return this.uploadStatus.map(s => s.total).reduce((a,b) => a+b, 1)
+ }
+
+ public dropped(files: NgxFileDropEntry[]) {
+ for (const droppedFile of files) {
+ if (droppedFile.fileEntry.isFile) {
+ let uploadStatusObject: UploadStatus = {loaded: 0, total: 1}
+ this.uploadStatus.push(uploadStatusObject)
+ this.uploadVisible = true
+
+ const fileEntry = droppedFile.fileEntry as FileSystemFileEntry;
+ fileEntry.file((file: File) => {
+ let formData = new FormData()
+ formData.append('document', file, file.name)
+
+ this.documentService.uploadDocument(formData).subscribe(event => {
+ if (event.type == HttpEventType.UploadProgress) {
+ uploadStatusObject.loaded = event.loaded
+ uploadStatusObject.total = event.total
+ } else if (event.type == HttpEventType.Response) {
+ this.uploadStatus.splice(this.uploadStatus.indexOf(uploadStatusObject), 1)
+ this.completedFiles += 1
+ this.toastService.showToast(Toast.make("Information", "The document has been uploaded and will be processed by the consumer shortly."))
+ }
+
+ }, error => {
+ this.uploadStatus.splice(this.uploadStatus.indexOf(uploadStatusObject), 1)
+ this.completedFiles += 1
+ switch (error.status) {
+ case 400: {
+ this.toastService.showToast(Toast.makeError(`There was an error while uploading the document: ${error.error.document}`))
+ break;
+ }
+ default: {
+ this.toastService.showToast(Toast.makeError("An error has occurred while uploading the document. Sorry!"))
+ break;
+ }
+ }
+ })
+ });
+ }
+ }
+ }
+}
diff --git a/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html
new file mode 100644
index 000000000..0caf55f11
--- /dev/null
+++ b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.html
@@ -0,0 +1,16 @@
+
+
+
+
+ Paperless is running! :)
+ You can start uploading documents by dropping them in the file upload box to the right or by dropping them in the configured consumption folder and they'll start showing up in the documents list.
+ After you've added some metadata to your documents, use the filtering mechanisms of paperless to create custom views (such as 'Recently added', 'Tagged TODO') and have them displayed on the dashboard instead of this message.
+ Paperless offers some more features that try to make your life easier, such as:
+
+ Once you've got a couple documents in paperless and added metadata to them, paperless can assign that metadata to new documents automatically.
+ You can configure paperless to read your mails and add documents from attached files.
+
+ Consult the documentation on how to use these features. The section on basic usage also has some information on how to use paperless in general.
+
+
+
\ No newline at end of file
diff --git a/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.scss b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.spec.ts b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.spec.ts
new file mode 100644
index 000000000..5e8c2494b
--- /dev/null
+++ b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { WelcomeWidgetComponent } from './welcome-widget.component';
+
+describe('WelcomeWidgetComponent', () => {
+ let component: WelcomeWidgetComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ WelcomeWidgetComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(WelcomeWidgetComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.ts b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.ts
new file mode 100644
index 000000000..71a87189c
--- /dev/null
+++ b/src-ui/src/app/components/dashboard/widgets/welcome-widget/welcome-widget.component.ts
@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'app-welcome-widget',
+ templateUrl: './welcome-widget.component.html',
+ styleUrls: ['./welcome-widget.component.scss']
+})
+export class WelcomeWidgetComponent implements OnInit {
+
+ constructor() { }
+
+ ngOnInit(): void {
+ }
+
+}
diff --git a/src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.html b/src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.html
new file mode 100644
index 000000000..1d7d2d906
--- /dev/null
+++ b/src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.html
@@ -0,0 +1,12 @@
+
\ No newline at end of file
diff --git a/src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.scss b/src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.spec.ts b/src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.spec.ts
new file mode 100644
index 000000000..ea1696750
--- /dev/null
+++ b/src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { WidgetFrameComponent } from './widget-frame.component';
+
+describe('WidgetFrameComponent', () => {
+ let component: WidgetFrameComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ WidgetFrameComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(WidgetFrameComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.ts b/src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.ts
new file mode 100644
index 000000000..38d6b0023
--- /dev/null
+++ b/src-ui/src/app/components/dashboard/widgets/widget-frame/widget-frame.component.ts
@@ -0,0 +1,18 @@
+import { Component, Input, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'app-widget-frame',
+ templateUrl: './widget-frame.component.html',
+ styleUrls: ['./widget-frame.component.scss']
+})
+export class WidgetFrameComponent implements OnInit {
+
+ constructor() { }
+
+ @Input()
+ title: string
+
+ ngOnInit(): void {
+ }
+
+}
diff --git a/src-ui/src/app/components/document-detail/document-detail.component.html b/src-ui/src/app/components/document-detail/document-detail.component.html
new file mode 100644
index 000000000..f4a64c2cc
--- /dev/null
+++ b/src-ui/src/app/components/document-detail/document-detail.component.html
@@ -0,0 +1,134 @@
+
+
+
+
+
+ Delete
+
+
+
+
+
+
+
+
+
+ Close
+
+
+
+
+
diff --git a/src-ui/src/app/components/document-detail/document-detail.component.scss b/src-ui/src/app/components/document-detail/document-detail.component.scss
new file mode 100644
index 000000000..998653bab
--- /dev/null
+++ b/src-ui/src/app/components/document-detail/document-detail.component.scss
@@ -0,0 +1,6 @@
+.pdf-viewer-container {
+ height: calc(100vh - 160px);
+ top: 70px;
+ position: sticky;
+ background-color: gray;
+}
diff --git a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts
new file mode 100644
index 000000000..0e4923e2e
--- /dev/null
+++ b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DocumentDetailComponent } from './document-detail.component';
+
+describe('DocumentDetailComponent', () => {
+ let component: DocumentDetailComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ DocumentDetailComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DocumentDetailComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts
new file mode 100644
index 000000000..b4005b920
--- /dev/null
+++ b/src-ui/src/app/components/document-detail/document-detail.component.ts
@@ -0,0 +1,174 @@
+import { Component, OnInit } from '@angular/core';
+import { FormControl, FormGroup } from '@angular/forms';
+import { ActivatedRoute, Router } from '@angular/router';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
+import { PaperlessDocument } from 'src/app/data/paperless-document';
+import { PaperlessDocumentMetadata } from 'src/app/data/paperless-document-metadata';
+import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
+import { DocumentTitlePipe } from 'src/app/pipes/document-title.pipe';
+import { DocumentListViewService } from 'src/app/services/document-list-view.service';
+import { OpenDocumentsService } from 'src/app/services/open-documents.service';
+import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
+import { DocumentTypeService } from 'src/app/services/rest/document-type.service';
+import { DocumentService } from 'src/app/services/rest/document.service';
+import { ConfirmDialogComponent } from '../common/confirm-dialog/confirm-dialog.component';
+import { CorrespondentEditDialogComponent } from '../manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component';
+import { DocumentTypeEditDialogComponent } from '../manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component';
+
+@Component({
+ selector: 'app-document-detail',
+ templateUrl: './document-detail.component.html',
+ styleUrls: ['./document-detail.component.scss']
+})
+export class DocumentDetailComponent implements OnInit {
+
+ public expandOriginalMetadata = false;
+ public expandArchivedMetadata = false;
+
+ documentId: number
+ document: PaperlessDocument
+ metadata: PaperlessDocumentMetadata
+ title: string
+ previewUrl: string
+ downloadUrl: string
+ downloadOriginalUrl: string
+
+ correspondents: PaperlessCorrespondent[]
+ documentTypes: PaperlessDocumentType[]
+
+ documentForm: FormGroup = new FormGroup({
+ title: new FormControl(''),
+ content: new FormControl(''),
+ created: new FormControl(),
+ correspondent: new FormControl(),
+ document_type: new FormControl(),
+ archive_serial_number: new FormControl(),
+ tags: new FormControl([])
+ })
+
+ constructor(
+ private documentsService: DocumentService,
+ private route: ActivatedRoute,
+ private correspondentService: CorrespondentService,
+ private documentTypeService: DocumentTypeService,
+ private router: Router,
+ private modalService: NgbModal,
+ private openDocumentService: OpenDocumentsService,
+ private documentListViewService: DocumentListViewService,
+ private documentTitlePipe: DocumentTitlePipe) { }
+
+ getContentType() {
+ return this.metadata?.has_archive_version ? 'application/pdf' : this.metadata?.original_mime_type
+ }
+
+ ngOnInit(): void {
+ this.documentForm.valueChanges.subscribe(wow => {
+ Object.assign(this.document, this.documentForm.value)
+ })
+
+ this.correspondentService.listAll().subscribe(result => this.correspondents = result.results)
+ this.documentTypeService.listAll().subscribe(result => this.documentTypes = result.results)
+
+ this.route.paramMap.subscribe(paramMap => {
+ this.documentId = +paramMap.get('id')
+ this.previewUrl = this.documentsService.getPreviewUrl(this.documentId)
+ this.downloadUrl = this.documentsService.getDownloadUrl(this.documentId)
+ this.downloadOriginalUrl = this.documentsService.getDownloadUrl(this.documentId, true)
+ if (this.openDocumentService.getOpenDocument(this.documentId)) {
+ this.updateComponent(this.openDocumentService.getOpenDocument(this.documentId))
+ } else {
+ this.documentsService.get(this.documentId).subscribe(doc => {
+ this.openDocumentService.openDocument(doc)
+ this.updateComponent(doc)
+ }, error => {this.router.navigate(['404'])})
+ }
+ })
+
+ }
+
+ updateComponent(doc: PaperlessDocument) {
+ this.document = doc
+ this.documentsService.getMetadata(doc.id).subscribe(result => {
+ this.metadata = result
+ })
+ this.title = this.documentTitlePipe.transform(doc.title)
+ this.documentForm.patchValue(doc)
+ }
+
+ createDocumentType() {
+ var modal = this.modalService.open(DocumentTypeEditDialogComponent, {backdrop: 'static'})
+ modal.componentInstance.dialogMode = 'create'
+ modal.componentInstance.success.subscribe(newDocumentType => {
+ this.documentTypeService.listAll().subscribe(documentTypes => {
+ this.documentTypes = documentTypes.results
+ this.documentForm.get('document_type').setValue(newDocumentType.id)
+ })
+ })
+ }
+
+ createCorrespondent() {
+ var modal = this.modalService.open(CorrespondentEditDialogComponent, {backdrop: 'static'})
+ modal.componentInstance.dialogMode = 'create'
+ modal.componentInstance.success.subscribe(newCorrespondent => {
+ this.correspondentService.listAll().subscribe(correspondents => {
+ this.correspondents = correspondents.results
+ this.documentForm.get('correspondent').setValue(newCorrespondent.id)
+ })
+ })
+ }
+
+ discard() {
+ this.documentsService.get(this.documentId).subscribe(doc => {
+ Object.assign(this.document, doc)
+ this.title = doc.title
+ this.documentForm.patchValue(doc)
+ }, error => {this.router.navigate(['404'])})
+ }
+
+ save() {
+ this.documentsService.update(this.document).subscribe(result => {
+ this.close()
+ })
+ }
+
+ saveEditNext() {
+ this.documentsService.update(this.document).subscribe(result => {
+ this.documentListViewService.getNext(this.document.id).subscribe(nextDocId => {
+ if (nextDocId) {
+ this.openDocumentService.closeDocument(this.document)
+ this.router.navigate(['documents', nextDocId])
+ }
+ })
+ })
+ }
+
+ close() {
+ this.openDocumentService.closeDocument(this.document)
+ if (this.documentListViewService.savedViewId) {
+ this.router.navigate(['view', this.documentListViewService.savedViewId])
+ } else {
+ this.router.navigate(['documents'])
+ }
+ }
+
+ delete() {
+ let modal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'})
+ modal.componentInstance.title = "Confirm delete"
+ modal.componentInstance.messageBold = `Do you really want to delete document '${this.document.title}'?`
+ modal.componentInstance.message = `The files for this document will be deleted permanently. This operation cannot be undone.`
+ modal.componentInstance.btnClass = "btn-danger"
+ modal.componentInstance.btnCaption = "Delete document"
+ modal.componentInstance.confirmClicked.subscribe(() => {
+ this.documentsService.delete(this.document).subscribe(() => {
+ modal.close()
+ this.close()
+ })
+ })
+
+ }
+
+ hasNext() {
+ return this.documentListViewService.hasNext(this.documentId)
+ }
+}
diff --git a/src-ui/src/app/components/document-detail/metadata-collapse/metadata-collapse.component.html b/src-ui/src/app/components/document-detail/metadata-collapse/metadata-collapse.component.html
new file mode 100644
index 000000000..e8fda1d0b
--- /dev/null
+++ b/src-ui/src/app/components/document-detail/metadata-collapse/metadata-collapse.component.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+ {{title}}
+
+
+
+
+
+
+ {{m.prefix}}:{{m.key}}
+ {{m.value}}
+
+
+
+
\ No newline at end of file
diff --git a/src-ui/src/app/components/document-detail/metadata-collapse/metadata-collapse.component.scss b/src-ui/src/app/components/document-detail/metadata-collapse/metadata-collapse.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src-ui/src/app/components/document-detail/metadata-collapse/metadata-collapse.component.spec.ts b/src-ui/src/app/components/document-detail/metadata-collapse/metadata-collapse.component.spec.ts
new file mode 100644
index 000000000..2bd96760b
--- /dev/null
+++ b/src-ui/src/app/components/document-detail/metadata-collapse/metadata-collapse.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MetadataCollapseComponent } from './metadata-collapse.component';
+
+describe('MetadataCollapseComponent', () => {
+ let component: MetadataCollapseComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ MetadataCollapseComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MetadataCollapseComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/document-detail/metadata-collapse/metadata-collapse.component.ts b/src-ui/src/app/components/document-detail/metadata-collapse/metadata-collapse.component.ts
new file mode 100644
index 000000000..160274e41
--- /dev/null
+++ b/src-ui/src/app/components/document-detail/metadata-collapse/metadata-collapse.component.ts
@@ -0,0 +1,23 @@
+import { Component, Input, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'app-metadata-collapse',
+ templateUrl: './metadata-collapse.component.html',
+ styleUrls: ['./metadata-collapse.component.scss']
+})
+export class MetadataCollapseComponent implements OnInit {
+
+ constructor() { }
+
+ expand = false
+
+ @Input()
+ metadata
+
+ @Input()
+ title = "Metadata"
+
+ ngOnInit(): void {
+ }
+
+}
diff --git a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html
new file mode 100644
index 000000000..c2645db5e
--- /dev/null
+++ b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{(document.correspondent$ | async)?.name}}
+ {{(document.correspondent$ | async)?.name}} :
+
+ {{document.title | documentTitle}}
+
+
+
#{{document.archive_serial_number}}
+
+
+
+ {{getDetailsAsString()}}
+
+
+
+
+
+
Created: {{document.created | date}}
+
+
+
+
+
+
diff --git a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.scss b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.scss
new file mode 100644
index 000000000..11fb10562
--- /dev/null
+++ b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.scss
@@ -0,0 +1,12 @@
+.result-content {
+ color: darkgray;
+ overflow-wrap: anywhere;
+}
+
+.doc-img {
+ object-fit: cover;
+ object-position: top;
+ height: 100%;
+ position: absolute;
+
+}
\ No newline at end of file
diff --git a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.spec.ts b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.spec.ts
new file mode 100644
index 000000000..72b48f139
--- /dev/null
+++ b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DocumentCardLargeComponent } from './document-card-large.component';
+
+describe('DocumentCardLargeComponent', () => {
+ let component: DocumentCardLargeComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ DocumentCardLargeComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DocumentCardLargeComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.ts b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.ts
new file mode 100644
index 000000000..2e056cc70
--- /dev/null
+++ b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.ts
@@ -0,0 +1,54 @@
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { DomSanitizer } from '@angular/platform-browser';
+import { PaperlessDocument } from 'src/app/data/paperless-document';
+import { DocumentService } from 'src/app/services/rest/document.service';
+
+@Component({
+ selector: 'app-document-card-large',
+ templateUrl: './document-card-large.component.html',
+ styleUrls: ['./document-card-large.component.scss']
+})
+export class DocumentCardLargeComponent implements OnInit {
+
+ constructor(private documentService: DocumentService, private sanitizer: DomSanitizer) { }
+
+ @Input()
+ document: PaperlessDocument
+
+ @Input()
+ details: any
+
+ @Output()
+ clickTag = new EventEmitter()
+
+ @Output()
+ clickCorrespondent = new EventEmitter()
+
+ ngOnInit(): void {
+ }
+
+ getDetailsAsString() {
+ if (typeof this.details === 'string') {
+ return this.details.substring(0, 500)
+ }
+ }
+
+ getDetailsAsHighlight() {
+ //TODO: this is not an exact typecheck, can we do better
+ if (this.details instanceof Array) {
+ return this.details
+ }
+ }
+
+ getThumbUrl() {
+ return this.documentService.getThumbUrl(this.document.id)
+ }
+
+ getDownloadUrl() {
+ return this.documentService.getDownloadUrl(this.document.id)
+ }
+
+ getPreviewUrl() {
+ return this.documentService.getPreviewUrl(this.document.id)
+ }
+}
diff --git a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html
new file mode 100644
index 000000000..2647e702c
--- /dev/null
+++ b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html
@@ -0,0 +1,50 @@
+
diff --git a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.scss b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.scss
new file mode 100644
index 000000000..0068667d0
--- /dev/null
+++ b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.scss
@@ -0,0 +1,5 @@
+.doc-img {
+ object-fit: cover;
+ object-position: top;
+ height: 200px;
+}
\ No newline at end of file
diff --git a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.spec.ts b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.spec.ts
new file mode 100644
index 000000000..4ed43b2e2
--- /dev/null
+++ b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DocumentCardSmallComponent } from './document-card-small.component';
+
+describe('DocumentCardSmallComponent', () => {
+ let component: DocumentCardSmallComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ DocumentCardSmallComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DocumentCardSmallComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.ts b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.ts
new file mode 100644
index 000000000..d87eb4331
--- /dev/null
+++ b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.ts
@@ -0,0 +1,54 @@
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { map } from 'rxjs/operators';
+import { PaperlessDocument } from 'src/app/data/paperless-document';
+import { DocumentService } from 'src/app/services/rest/document.service';
+
+@Component({
+ selector: 'app-document-card-small',
+ templateUrl: './document-card-small.component.html',
+ styleUrls: ['./document-card-small.component.scss']
+})
+export class DocumentCardSmallComponent implements OnInit {
+
+ constructor(private documentService: DocumentService) { }
+
+ @Input()
+ document: PaperlessDocument
+
+ @Output()
+ clickTag = new EventEmitter()
+
+ @Output()
+ clickCorrespondent = new EventEmitter()
+
+ moreTags: number = null
+
+ ngOnInit(): void {
+ }
+
+ getThumbUrl() {
+ return this.documentService.getThumbUrl(this.document.id)
+ }
+
+ getDownloadUrl() {
+ return this.documentService.getDownloadUrl(this.document.id)
+ }
+
+ getPreviewUrl() {
+ return this.documentService.getPreviewUrl(this.document.id)
+ }
+
+ getTagsLimited$() {
+ return this.document.tags$.pipe(
+ map(tags => {
+ if (tags.length > 7) {
+ this.moreTags = tags.length - 6
+ return tags.slice(0, 6)
+ } else {
+ return tags
+ }
+ })
+ )
+ }
+
+}
diff --git a/src-ui/src/app/components/document-list/document-list.component.html b/src-ui/src/app/components/document-list/document-list.component.html
new file mode 100644
index 000000000..5c09f6c13
--- /dev/null
+++ b/src-ui/src/app/components/document-list/document-list.component.html
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Sort by
+
+ {{f.name}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Views
+
+
+ {{view.name}}
+ 0">
+
+
+
Save "{{list.savedViewTitle}}"
+
Save as...
+
+
+
+
+
+
+
+
+
+
+
{{list.collectionSize || 0}} document(s) (filtered)
+
+
+
+
+
+
+
+
+
diff --git a/src-ui/src/app/components/document-list/document-list.component.scss b/src-ui/src/app/components/document-list/document-list.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src-ui/src/app/components/document-list/document-list.component.spec.ts b/src-ui/src/app/components/document-list/document-list.component.spec.ts
new file mode 100644
index 000000000..9230b4631
--- /dev/null
+++ b/src-ui/src/app/components/document-list/document-list.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DocumentListComponent } from './document-list.component';
+
+describe('DocumentListComponent', () => {
+ let component: DocumentListComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ DocumentListComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DocumentListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/document-list/document-list.component.ts b/src-ui/src/app/components/document-list/document-list.component.ts
new file mode 100644
index 000000000..25d92e9db
--- /dev/null
+++ b/src-ui/src/app/components/document-list/document-list.component.ts
@@ -0,0 +1,116 @@
+import { Component, OnInit, ViewChild } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { FILTER_CORRESPONDENT } from 'src/app/data/filter-rule-type';
+import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
+import { DocumentListViewService } from 'src/app/services/document-list-view.service';
+import { DOCUMENT_SORT_FIELDS } from 'src/app/services/rest/document.service';
+import { SavedViewService } from 'src/app/services/rest/saved-view.service';
+import { Toast, ToastService } from 'src/app/services/toast.service';
+import { FilterEditorComponent } from '../filter-editor/filter-editor.component';
+import { SaveViewConfigDialogComponent } from './save-view-config-dialog/save-view-config-dialog.component';
+
+@Component({
+ selector: 'app-document-list',
+ templateUrl: './document-list.component.html',
+ styleUrls: ['./document-list.component.scss']
+})
+export class DocumentListComponent implements OnInit {
+
+ constructor(
+ public list: DocumentListViewService,
+ public savedViewService: SavedViewService,
+ public route: ActivatedRoute,
+ private router: Router,
+ private toastService: ToastService,
+ public modalService: NgbModal) { }
+
+ @ViewChild("filterEditor")
+ private filterEditor: FilterEditorComponent
+
+ displayMode = 'smallCards' // largeCards, smallCards, details
+
+ get isFiltered() {
+ return this.list.filterRules?.length > 0
+ }
+
+ getTitle() {
+ return this.list.savedViewTitle || "Documents"
+ }
+
+ getSortFields() {
+ return DOCUMENT_SORT_FIELDS
+ }
+
+ saveDisplayMode() {
+ localStorage.setItem('document-list:displayMode', this.displayMode)
+ }
+
+ ngOnInit(): void {
+ if (localStorage.getItem('document-list:displayMode') != null) {
+ this.displayMode = localStorage.getItem('document-list:displayMode')
+ }
+ this.route.paramMap.subscribe(params => {
+ this.list.clear()
+ if (params.has('id')) {
+ this.savedViewService.getCached(+params.get('id')).subscribe(view => {
+ if (!view) {
+ this.router.navigate(["404"])
+ return
+ }
+
+ this.list.savedView = view
+ this.list.reload()
+ })
+ } else {
+ this.list.savedView = null
+ this.list.reload()
+ }
+ })
+ }
+
+
+ loadViewConfig(view: PaperlessSavedView) {
+ this.list.load(view)
+ this.list.reload()
+ }
+
+ saveViewConfig() {
+ this.savedViewService.update(this.list.savedView).subscribe(result => {
+ this.toastService.showToast(Toast.make("Information", `View "${this.list.savedView.name}" saved successfully.`))
+ })
+
+ }
+
+ saveViewConfigAs() {
+ let modal = this.modalService.open(SaveViewConfigDialogComponent, {backdrop: 'static'})
+ modal.componentInstance.defaultName = this.filterEditor.generateFilterName()
+ modal.componentInstance.saveClicked.subscribe(formValue => {
+ let savedView = {
+ name: formValue.name,
+ show_on_dashboard: formValue.showOnDashboard,
+ show_in_sidebar: formValue.showInSideBar,
+ filter_rules: this.list.filterRules,
+ sort_reverse: this.list.sortReverse,
+ sort_field: this.list.sortField
+ }
+ this.savedViewService.create(savedView).subscribe(() => {
+ modal.close()
+ this.toastService.showToast(Toast.make("Information", `View "${savedView.name}" created successfully.`))
+ })
+ })
+ }
+
+ clickTag(tagID: number) {
+ this.filterEditor.toggleTag(tagID)
+ }
+
+ clickCorrespondent(correspondentID: number) {
+ this.filterEditor.toggleCorrespondent(correspondentID)
+ }
+
+ clickDocumentType(documentTypeID: number) {
+ this.filterEditor.toggleDocumentType(documentTypeID)
+ }
+
+}
diff --git a/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html b/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html
new file mode 100644
index 000000000..8819aa313
--- /dev/null
+++ b/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html
@@ -0,0 +1,17 @@
+
+
+
+
+
diff --git a/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.scss b/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.spec.ts b/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.spec.ts
new file mode 100644
index 000000000..11ac77c0b
--- /dev/null
+++ b/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SaveViewConfigDialogComponent } from './save-view-config-dialog.component';
+
+describe('SaveViewConfigDialogComponent', () => {
+ let component: SaveViewConfigDialogComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ SaveViewConfigDialogComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(SaveViewConfigDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.ts b/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.ts
new file mode 100644
index 000000000..8f0eb26f2
--- /dev/null
+++ b/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.ts
@@ -0,0 +1,46 @@
+import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
+import { FormControl, FormGroup } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+
+@Component({
+ selector: 'app-save-view-config-dialog',
+ templateUrl: './save-view-config-dialog.component.html',
+ styleUrls: ['./save-view-config-dialog.component.scss']
+})
+export class SaveViewConfigDialogComponent implements OnInit {
+
+ constructor(private modal: NgbActiveModal) { }
+
+ @Output()
+ public saveClicked = new EventEmitter()
+
+ _defaultName = ""
+
+ get defaultName() {
+ return this._defaultName
+ }
+
+ @Input()
+ set defaultName(value: string) {
+ this._defaultName = value
+ this.saveViewConfigForm.patchValue({name: value})
+ }
+
+
+ saveViewConfigForm = new FormGroup({
+ name: new FormControl(''),
+ showInSideBar: new FormControl(false),
+ showOnDashboard: new FormControl(false),
+ })
+
+ ngOnInit(): void {
+ }
+
+ save() {
+ this.saveClicked.emit(this.saveViewConfigForm.value)
+ }
+
+ cancel() {
+ this.modal.close()
+ }
+}
diff --git a/src-ui/src/app/components/filter-editor/filter-dropdown-date/filter-dropdown-date.component.html b/src-ui/src/app/components/filter-editor/filter-dropdown-date/filter-dropdown-date.component.html
new file mode 100644
index 000000000..6f6a42fe2
--- /dev/null
+++ b/src-ui/src/app/components/filter-editor/filter-dropdown-date/filter-dropdown-date.component.html
@@ -0,0 +1,43 @@
+
+
+ {{title}}
+
+
+
+
Clear
+
+ This
+ {{ range }}
+ days
+
+
+
+
+
+
diff --git a/src-ui/src/app/components/filter-editor/filter-dropdown-date/filter-dropdown-date.component.scss b/src-ui/src/app/components/filter-editor/filter-dropdown-date/filter-dropdown-date.component.scss
new file mode 100644
index 000000000..3bdedd8a0
--- /dev/null
+++ b/src-ui/src/app/components/filter-editor/filter-dropdown-date/filter-dropdown-date.component.scss
@@ -0,0 +1,7 @@
+.date-filter {
+ min-width: 250px;
+
+ .btn-link {
+ line-height: 1;
+ }
+}
diff --git a/src-ui/src/app/components/filter-editor/filter-dropdown-date/filter-dropdown-date.component.spec.ts b/src-ui/src/app/components/filter-editor/filter-dropdown-date/filter-dropdown-date.component.spec.ts
new file mode 100644
index 000000000..6bf59e2e7
--- /dev/null
+++ b/src-ui/src/app/components/filter-editor/filter-dropdown-date/filter-dropdown-date.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FilterDropdownDateComponent } from './filter-dropdown-date.component';
+
+describe('FilterDropdownDateComponent', () => {
+ let component: FilterDropdownDateComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ FilterDropdownDateComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(FilterDropdownDateComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/filter-editor/filter-dropdown-date/filter-dropdown-date.component.ts b/src-ui/src/app/components/filter-editor/filter-dropdown-date/filter-dropdown-date.component.ts
new file mode 100644
index 000000000..91402d084
--- /dev/null
+++ b/src-ui/src/app/components/filter-editor/filter-dropdown-date/filter-dropdown-date.component.ts
@@ -0,0 +1,112 @@
+import { Component, EventEmitter, Input, Output, ElementRef, ViewChild, SimpleChange } from '@angular/core';
+import { NgbDate, NgbDateStruct, NgbDatepicker } from '@ng-bootstrap/ng-bootstrap';
+
+export interface DateSelection {
+ before?: NgbDateStruct
+ after?: NgbDateStruct
+}
+
+
+@Component({
+ selector: 'app-filter-dropdown-date',
+ templateUrl: './filter-dropdown-date.component.html',
+ styleUrls: ['./filter-dropdown-date.component.scss']
+})
+export class FilterDropdownDateComponent {
+
+ @Input()
+ dateBefore: NgbDateStruct
+
+ @Input()
+ dateAfter: NgbDateStruct
+
+ @Input()
+ title: string
+
+ @Output()
+ datesSet = new EventEmitter()
+
+ @ViewChild('dpAfter') dpAfter: NgbDatepicker
+ @ViewChild('dpBefore') dpBefore: NgbDatepicker
+
+ _dateBefore: NgbDateStruct
+ _dateAfter: NgbDateStruct
+
+ get _maxDate(): NgbDate {
+ let date = new Date()
+ return NgbDate.from({year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate()})
+ }
+
+ isStringRange(range: any) {
+ return typeof range == 'string'
+ }
+
+ ngOnChanges(changes: SimpleChange) {
+ // this is a hacky workaround perhaps because of https://github.com/angular/angular/issues/11097
+ let dateString: string = ''
+ let dateAfterChange: SimpleChange
+ let dateBeforeChange: SimpleChange
+ if (changes) {
+ dateAfterChange = changes['dateAfter']
+ dateBeforeChange = changes['dateBefore']
+ }
+
+ if (this.dpBefore && this.dpAfter) {
+ let dpAfterElRef: ElementRef = this.dpAfter['_elRef']
+ let dpBeforeElRef: ElementRef = this.dpBefore['_elRef']
+
+ if (dateAfterChange && dateAfterChange.currentValue) {
+ let dateAfterDate = dateAfterChange.currentValue as NgbDateStruct
+ dateString = `${dateAfterDate.year}-${dateAfterDate.month.toString().padStart(2,'0')}-${dateAfterDate.day.toString().padStart(2,'0')}`
+ dpAfterElRef.nativeElement.value = dateString
+ } else if (dateBeforeChange && dateBeforeChange.currentValue) {
+ let dateBeforeDate = dateBeforeChange.currentValue as NgbDateStruct
+ dateString = `${dateBeforeDate.year}-${dateBeforeDate.month.toString().padStart(2,'0')}-${dateBeforeDate.day.toString().padStart(2,'0')}`
+ dpBeforeElRef.nativeElement.value = dateString
+ } else {
+ dpAfterElRef.nativeElement.value = dateString
+ dpBeforeElRef.nativeElement.value = dateString
+ }
+ }
+ }
+
+ setDateQuickFilter(range: any) {
+ let date = new Date()
+ let newDate: NgbDateStruct = { year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate() }
+ switch (typeof range) {
+ case 'number':
+ date.setDate(date.getDate() - range)
+ newDate.year = date.getFullYear()
+ newDate.month = date.getMonth() + 1
+ newDate.day = date.getDate()
+ break
+
+ case 'string':
+ newDate.day = 1
+ if (range == 'year') newDate.month = 1
+ break
+
+ default:
+ break
+ }
+ this._dateAfter = newDate
+ this._dateBefore = null
+ this.datesSet.emit({after: newDate, before: null})
+ }
+
+ onBeforeSelected(date: NgbDateStruct) {
+ this._dateBefore = date
+ this.datesSet.emit({after: this._dateAfter, before: date})
+ }
+
+ onAfterSelected(date: NgbDateStruct) {
+ this._dateAfter = date
+ this.datesSet.emit({after: date, before: this._dateBefore})
+ }
+
+ clear() {
+ this._dateBefore = null
+ this._dateAfter = null
+ this.datesSet.emit({after: null, before: null})
+ }
+}
diff --git a/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown-button/filter-dropdown-button.component.html b/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown-button/filter-dropdown-button.component.html
new file mode 100644
index 000000000..8dff12a33
--- /dev/null
+++ b/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown-button/filter-dropdown-button.component.html
@@ -0,0 +1,12 @@
+
+
+
+ {{item.document_count}}
+
diff --git a/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown-button/filter-dropdown-button.component.scss b/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown-button/filter-dropdown-button.component.scss
new file mode 100644
index 000000000..41fc6acc4
--- /dev/null
+++ b/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown-button/filter-dropdown-button.component.scss
@@ -0,0 +1,4 @@
+.selected-icon {
+ min-width: 1em;
+ min-height: 1em;
+}
diff --git a/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown-button/filter-dropdown-button.component.spec.ts b/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown-button/filter-dropdown-button.component.spec.ts
new file mode 100644
index 000000000..5cf1fefa2
--- /dev/null
+++ b/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown-button/filter-dropdown-button.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FilterDropodownButtonComponent } from './filter-dropdown-button.component';
+
+describe('FilterDropodownButtonComponent', () => {
+ let component: FilterDropodownButtonComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ FilterDropodownButtonComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(FilterDropodownButtonComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown-button/filter-dropdown-button.component.ts b/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown-button/filter-dropdown-button.component.ts
new file mode 100644
index 000000000..d3ddd3cbf
--- /dev/null
+++ b/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown-button/filter-dropdown-button.component.ts
@@ -0,0 +1,32 @@
+import { Component, EventEmitter, Input, Output, OnInit } from '@angular/core';
+import { PaperlessTag } from 'src/app/data/paperless-tag';
+import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
+import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
+
+@Component({
+ selector: 'app-filter-dropdown-button',
+ templateUrl: './filter-dropdown-button.component.html',
+ styleUrls: ['./filter-dropdown-button.component.scss']
+})
+export class FilterDropdownButtonComponent implements OnInit {
+
+ @Input()
+ item: PaperlessTag | PaperlessDocumentType | PaperlessCorrespondent
+
+ @Input()
+ selected: boolean
+
+ @Output()
+ toggle = new EventEmitter()
+
+ isTag: boolean
+
+ ngOnInit() {
+ this.isTag = 'is_inbox_tag' in this.item // ~ this.item instanceof PaperlessTag
+ }
+
+ toggleItem(): void {
+ this.selected = !this.selected
+ this.toggle.emit(this.item)
+ }
+}
diff --git a/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown.component.html b/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown.component.html
new file mode 100644
index 000000000..d0cbfc3c9
--- /dev/null
+++ b/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown.component.html
@@ -0,0 +1,29 @@
+
+
0 ? 'btn-primary' : 'btn-outline-primary'">
+ {{title}}
+
+
+
+
+
+ 0">
+
+ {{itemsSelected?.length}}
+
+
+
+
+
diff --git a/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown.component.scss b/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown.component.scss
new file mode 100644
index 000000000..40c93838f
--- /dev/null
+++ b/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown.component.scss
@@ -0,0 +1,14 @@
+.badge-corner {
+ position: absolute;
+ top: -8px;
+ right: -8px;
+}
+
+.dropdown-menu {
+ min-width: 250px;
+
+ .items {
+ max-height: 400px;
+ overflow-y: scroll;
+ }
+}
diff --git a/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown.component.spec.ts b/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown.component.spec.ts
new file mode 100644
index 000000000..29edd7c45
--- /dev/null
+++ b/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FilterDropodownComponent } from './filter-dropdown.component';
+
+describe('FilterDropodownComponent', () => {
+ let component: FilterDropodownComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ FilterDropodownComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(FilterDropodownComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown.component.ts b/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown.component.ts
new file mode 100644
index 000000000..b9d3fca6f
--- /dev/null
+++ b/src-ui/src/app/components/filter-editor/filter-dropdown/filter-dropdown.component.ts
@@ -0,0 +1,58 @@
+import { Component, EventEmitter, Input, Output, ElementRef, ViewChild } from '@angular/core';
+import { ObjectWithId } from 'src/app/data/object-with-id';
+import { FilterPipe } from 'src/app/pipes/filter.pipe';
+import { NgbDropdown } from '@ng-bootstrap/ng-bootstrap'
+
+@Component({
+ selector: 'app-filter-dropdown',
+ templateUrl: './filter-dropdown.component.html',
+ styleUrls: ['./filter-dropdown.component.scss']
+})
+export class FilterDropdownComponent {
+
+ constructor(private filterPipe: FilterPipe) { }
+
+ @Input()
+ items: ObjectWithId[]
+
+ @Input()
+ itemsSelected: ObjectWithId[]
+
+ @Input()
+ title: string
+
+ @Input()
+ icon: string
+
+ @Output()
+ toggle = new EventEmitter()
+
+ @ViewChild('listFilterTextInput') listFilterTextInput: ElementRef
+ @ViewChild('filterDropdown') filterDropdown: NgbDropdown
+
+ filterText: string
+
+ toggleItem(item: ObjectWithId): void {
+ this.toggle.emit(item)
+ }
+
+ isItemSelected(item: ObjectWithId): boolean {
+ return this.itemsSelected?.find(i => i.id == item.id) !== undefined
+ }
+
+ dropdownOpenChange(open: boolean): void {
+ if (open) {
+ setTimeout(() => {
+ this.listFilterTextInput.nativeElement.focus();
+ }, 0);
+ } else {
+ this.filterText = ''
+ }
+ }
+
+ listFilterEnter(): void {
+ let filtered = this.filterPipe.transform(this.items, this.filterText)
+ if (filtered.length == 1) this.toggleItem(filtered.shift())
+ this.filterDropdown.close()
+ }
+}
diff --git a/src-ui/src/app/components/filter-editor/filter-editor.component.html b/src-ui/src/app/components/filter-editor/filter-editor.component.html
new file mode 100644
index 000000000..6847a2902
--- /dev/null
+++ b/src-ui/src/app/components/filter-editor/filter-editor.component.html
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+ Clear all filters
+
+
+
diff --git a/src-ui/src/app/components/filter-editor/filter-editor.component.scss b/src-ui/src/app/components/filter-editor/filter-editor.component.scss
new file mode 100644
index 000000000..05df7b213
--- /dev/null
+++ b/src-ui/src/app/components/filter-editor/filter-editor.component.scss
@@ -0,0 +1,10 @@
+.quick-filter {
+ min-width: 250px;
+ max-height: 400px;
+ overflow-y: scroll;
+
+ .selected-icon {
+ min-width: 1em;
+ min-height: 1em;
+ }
+}
diff --git a/src-ui/src/app/components/filter-editor/filter-editor.component.spec.ts b/src-ui/src/app/components/filter-editor/filter-editor.component.spec.ts
new file mode 100644
index 000000000..9aee51435
--- /dev/null
+++ b/src-ui/src/app/components/filter-editor/filter-editor.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FilterEditorComponent } from './filter-editor.component';
+
+describe('FilterEditorComponent', () => {
+ let component: FilterEditorComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ FilterEditorComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(FilterEditorComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/filter-editor/filter-editor.component.ts b/src-ui/src/app/components/filter-editor/filter-editor.component.ts
new file mode 100644
index 000000000..f762c6138
--- /dev/null
+++ b/src-ui/src/app/components/filter-editor/filter-editor.component.ts
@@ -0,0 +1,240 @@
+import { Component, EventEmitter, Input, Output, OnInit, OnDestroy } from '@angular/core';
+import { PaperlessTag } from 'src/app/data/paperless-tag';
+import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
+import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
+import { Subject, Subscription } from 'rxjs';
+import { debounceTime, distinctUntilChanged } from 'rxjs/operators';
+import { NgbDateParserFormatter, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap';
+import { DocumentTypeService } from 'src/app/services/rest/document-type.service';
+import { TagService } from 'src/app/services/rest/tag.service';
+import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
+import { FilterRule } from 'src/app/data/filter-rule';
+import { FILTER_ADDED_AFTER, FILTER_ADDED_BEFORE, FILTER_CORRESPONDENT, FILTER_CREATED_AFTER, FILTER_CREATED_BEFORE, FILTER_DOCUMENT_TYPE, FILTER_HAS_TAG, FILTER_RULE_TYPES, FILTER_TITLE } from 'src/app/data/filter-rule-type';
+import { DateSelection } from './filter-dropdown-date/filter-dropdown-date.component';
+
+@Component({
+ selector: 'app-filter-editor',
+ templateUrl: './filter-editor.component.html',
+ styleUrls: ['./filter-editor.component.scss']
+})
+export class FilterEditorComponent implements OnInit, OnDestroy {
+
+ generateFilterName() {
+ if (this.filterRules.length == 1) {
+ let rule = this.filterRules[0]
+ switch(this.filterRules[0].rule_type) {
+
+ case FILTER_CORRESPONDENT:
+ return `Correspondent: ${this.correspondents.find(c => c.id == +rule.value)?.name}`
+
+ case FILTER_DOCUMENT_TYPE:
+ return `Type: ${this.documentTypes.find(dt => dt.id == +rule.value)?.name}`
+
+ case FILTER_HAS_TAG:
+ return `Tag: ${this.tags.find(t => t.id == +rule.value)?.name}`
+
+ }
+ }
+
+ return ""
+ }
+
+ constructor(
+ private documentTypeService: DocumentTypeService,
+ private tagService: TagService,
+ private correspondentService: CorrespondentService,
+ private dateParser: NgbDateParserFormatter
+ ) { }
+
+ tags: PaperlessTag[] = []
+ correspondents: PaperlessCorrespondent[]
+ documentTypes: PaperlessDocumentType[] = []
+
+ @Input()
+ filterRules: FilterRule[]
+
+ @Output()
+ filterRulesChange = new EventEmitter()
+
+ hasFilters() {
+ return this.filterRules.length > 0
+ }
+
+ get selectedTags(): PaperlessTag[] {
+ let tagRules: FilterRule[] = this.filterRules.filter(fr => fr.rule_type == FILTER_HAS_TAG)
+ return this.tags?.filter(t => tagRules.find(tr => +tr.value == t.id))
+ }
+
+ get selectedCorrespondents(): PaperlessCorrespondent[] {
+ let correspondentRules: FilterRule[] = this.filterRules.filter(fr => fr.rule_type == FILTER_CORRESPONDENT)
+ return this.correspondents?.filter(c => correspondentRules.find(cr => +cr.value == c.id))
+ }
+
+ get selectedDocumentTypes(): PaperlessDocumentType[] {
+ let documentTypeRules: FilterRule[] = this.filterRules.filter(fr => fr.rule_type == FILTER_DOCUMENT_TYPE)
+ return this.documentTypes?.filter(dt => documentTypeRules.find(dtr => +dtr.value == dt.id))
+ }
+
+ get titleFilter() {
+ let existingRule = this.filterRules.find(rule => rule.rule_type == FILTER_TITLE)
+ return existingRule ? existingRule.value : ''
+ }
+
+ set titleFilter(value) {
+ this.titleFilterDebounce.next(value)
+ }
+
+ titleFilterDebounce: Subject
+ subscription: Subscription
+
+ ngOnInit() {
+ this.tagService.listAll().subscribe(result => this.tags = result.results)
+ this.correspondentService.listAll().subscribe(result => this.correspondents = result.results)
+ this.documentTypeService.listAll().subscribe(result => this.documentTypes = result.results)
+
+ this.titleFilterDebounce = new Subject()
+
+ this.subscription = this.titleFilterDebounce.pipe(
+ debounceTime(400),
+ distinctUntilChanged()
+ ).subscribe(title => {
+ this.setTitleRule(title)
+ })
+ }
+
+ ngOnDestroy() {
+ this.titleFilterDebounce.complete()
+ // TODO: not sure if both is necessary
+ this.subscription.unsubscribe()
+ }
+
+ applyFilters() {
+ this.filterRulesChange.next(this.filterRules)
+ }
+
+ clearSelected() {
+ this.filterRules = []
+ this.applyFilters()
+ }
+
+ private toggleFilterRule(filterRuleTypeID: number, value: number) {
+
+ let filterRuleType = FILTER_RULE_TYPES.find(t => t.id == filterRuleTypeID)
+
+ let existingRule = this.filterRules.find(rule => rule.rule_type == filterRuleTypeID && rule.value == value?.toString())
+ let existingRuleOfSameType = this.filterRules.find(rule => rule.rule_type == filterRuleTypeID)
+
+ if (existingRule) {
+ // if this exact rule already exists, remove it in all cases.
+ this.filterRules.splice(this.filterRules.indexOf(existingRule), 1)
+ } else if (filterRuleType.multi || !existingRuleOfSameType) {
+ // if we allow multiple rules per type, or no rule of this type already exists, push a new rule.
+ this.filterRules.push({rule_type: filterRuleTypeID, value: value?.toString()})
+ } else {
+ // otherwise (i.e., no multi support AND there's already a rule of this type), update the rule.
+ existingRuleOfSameType.value = value?.toString()
+ }
+ this.applyFilters()
+ }
+
+ private setTitleRule(title: string) {
+ let existingRule = this.filterRules.find(rule => rule.rule_type == FILTER_TITLE)
+
+ if (!existingRule && title) {
+ this.filterRules.push({rule_type: FILTER_TITLE, value: title})
+ } else if (existingRule && !title) {
+ this.filterRules.splice(this.filterRules.findIndex(rule => rule.rule_type == FILTER_TITLE), 1)
+ } else if (existingRule && title) {
+ existingRule.value = title
+ }
+ this.applyFilters()
+ }
+
+ toggleTag(tagId: number) {
+ this.toggleFilterRule(FILTER_HAS_TAG, tagId)
+ }
+
+ toggleCorrespondent(correspondentId: number) {
+ this.toggleFilterRule(FILTER_CORRESPONDENT, correspondentId)
+ }
+
+ toggleDocumentType(documentTypeId: number) {
+ this.toggleFilterRule(FILTER_DOCUMENT_TYPE, documentTypeId)
+ }
+
+
+
+ // Date handling
+
+
+ onDatesCreatedSet(dates: DateSelection) {
+ this.setDateCreatedBefore(dates.before)
+ this.setDateCreatedAfter(dates.after)
+ this.applyFilters()
+ }
+
+ onDatesAddedSet(dates: DateSelection) {
+ this.setDateAddedBefore(dates.before)
+ this.setDateAddedAfter(dates.after)
+ this.applyFilters()
+ }
+
+ get dateCreatedBefore(): NgbDateStruct {
+ let createdBeforeRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_CREATED_BEFORE)
+ return createdBeforeRule ? this.dateParser.parse(createdBeforeRule.value) : null
+ }
+
+ get dateCreatedAfter(): NgbDateStruct {
+ let createdAfterRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_CREATED_AFTER)
+ return createdAfterRule ? this.dateParser.parse(createdAfterRule.value) : null
+ }
+
+ get dateAddedBefore(): NgbDateStruct {
+ let addedBeforeRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_ADDED_BEFORE)
+ return addedBeforeRule ? this.dateParser.parse(addedBeforeRule.value) : null
+ }
+
+ get dateAddedAfter(): NgbDateStruct {
+ let addedAfterRule: FilterRule = this.filterRules.find(fr => fr.rule_type == FILTER_ADDED_AFTER)
+ return addedAfterRule ? this.dateParser.parse(addedAfterRule.value) : null
+ }
+
+ setDateCreatedBefore(date?: NgbDateStruct) {
+ if (date) this.setDateFilter(date, FILTER_CREATED_BEFORE)
+ else this.clearDateFilter(FILTER_CREATED_BEFORE)
+ }
+
+ setDateCreatedAfter(date?: NgbDateStruct) {
+ if (date) this.setDateFilter(date, FILTER_CREATED_AFTER)
+ else this.clearDateFilter(FILTER_CREATED_AFTER)
+ }
+
+ setDateAddedBefore(date?: NgbDateStruct) {
+ if (date) this.setDateFilter(date, FILTER_ADDED_BEFORE)
+ else this.clearDateFilter(FILTER_ADDED_BEFORE)
+ }
+
+ setDateAddedAfter(date?: NgbDateStruct) {
+ if (date) this.setDateFilter(date, FILTER_ADDED_AFTER)
+ else this.clearDateFilter(FILTER_ADDED_AFTER)
+ }
+
+ setDateFilter(date: NgbDateStruct, dateRuleTypeID: number) {
+ let existingRule = this.filterRules.find(rule => rule.rule_type == dateRuleTypeID)
+ let newValue = this.dateParser.format(date)
+
+ if (existingRule) {
+ existingRule.value = newValue
+ } else {
+ this.filterRules.push({rule_type: dateRuleTypeID, value: newValue})
+ }
+ }
+
+ clearDateFilter(dateRuleTypeID: number) {
+ let ruleIndex = this.filterRules.findIndex(rule => rule.rule_type == dateRuleTypeID)
+ if (ruleIndex != -1) {
+ this.filterRules.splice(ruleIndex, 1)
+ }
+ }
+
+}
diff --git a/src-ui/src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html b/src-ui/src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html
new file mode 100644
index 000000000..307c78c3c
--- /dev/null
+++ b/src-ui/src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.html
@@ -0,0 +1,20 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src-ui/src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.scss b/src-ui/src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src-ui/src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.spec.ts b/src-ui/src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.spec.ts
new file mode 100644
index 000000000..b400b80fe
--- /dev/null
+++ b/src-ui/src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { CorrespondentEditDialogComponent } from './correspondent-edit-dialog.component';
+
+describe('CorrespondentEditDialogComponent', () => {
+ let component: CorrespondentEditDialogComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ CorrespondentEditDialogComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CorrespondentEditDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.ts b/src-ui/src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.ts
new file mode 100644
index 000000000..bc6b2a823
--- /dev/null
+++ b/src-ui/src/app/components/manage/correspondent-list/correspondent-edit-dialog/correspondent-edit-dialog.component.ts
@@ -0,0 +1,29 @@
+import { Component } from '@angular/core';
+import { FormControl, FormGroup } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component';
+import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
+import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
+import { ToastService } from 'src/app/services/toast.service';
+
+@Component({
+ selector: 'app-correspondent-edit-dialog',
+ templateUrl: './correspondent-edit-dialog.component.html',
+ styleUrls: ['./correspondent-edit-dialog.component.scss']
+})
+export class CorrespondentEditDialogComponent extends EditDialogComponent {
+
+ constructor(service: CorrespondentService, activeModal: NgbActiveModal, toastService: ToastService) {
+ super(service, activeModal, toastService, 'correspondent')
+ }
+
+ getForm(): FormGroup {
+ return new FormGroup({
+ name: new FormControl(''),
+ matching_algorithm: new FormControl(1),
+ match: new FormControl(""),
+ is_insensitive: new FormControl(true)
+ })
+ }
+
+}
diff --git a/src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.html b/src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.html
new file mode 100644
index 000000000..2efd1c58d
--- /dev/null
+++ b/src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.html
@@ -0,0 +1,52 @@
+
+
+ Create
+
+
+
+
+
+
+
+
+
+
+ Name
+ Matching
+ Document count
+ Last correspondence
+ Actions
+
+
+
+
+ {{ correspondent.name }}
+ {{ getMatching(correspondent) }}
+ {{ correspondent.document_count }}
+ {{ correspondent.last_correspondence | date }}
+
+
+
+
+
+
+ Documents
+
+
+
+
+
+ Edit
+
+
+
+
+
+
+ Delete
+
+
+
+
+
+
diff --git a/src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.scss b/src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.spec.ts b/src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.spec.ts
new file mode 100644
index 000000000..448696dde
--- /dev/null
+++ b/src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { CorrespondentListComponent } from './correspondent-list.component';
+
+describe('CorrespondentListComponent', () => {
+ let component: CorrespondentListComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ CorrespondentListComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CorrespondentListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.ts b/src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.ts
new file mode 100644
index 000000000..a128340b9
--- /dev/null
+++ b/src-ui/src/app/components/manage/correspondent-list/correspondent-list.component.ts
@@ -0,0 +1,35 @@
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { FILTER_CORRESPONDENT } from 'src/app/data/filter-rule-type';
+import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
+import { DocumentListViewService } from 'src/app/services/document-list-view.service';
+import { CorrespondentService } from 'src/app/services/rest/correspondent.service';
+import { GenericListComponent } from '../generic-list/generic-list.component';
+import { CorrespondentEditDialogComponent } from './correspondent-edit-dialog/correspondent-edit-dialog.component';
+
+@Component({
+ selector: 'app-correspondent-list',
+ templateUrl: './correspondent-list.component.html',
+ styleUrls: ['./correspondent-list.component.scss']
+})
+export class CorrespondentListComponent extends GenericListComponent {
+
+ constructor(correspondentsService: CorrespondentService, modalService: NgbModal,
+ private router: Router,
+ private list: DocumentListViewService
+ ) {
+ super(correspondentsService,modalService,CorrespondentEditDialogComponent)
+ }
+
+ getObjectName(object: PaperlessCorrespondent) {
+ return `correspondent '${object.name}'`
+ }
+
+ filterDocuments(object: PaperlessCorrespondent) {
+ this.list.documentListView.filter_rules = [
+ {rule_type: FILTER_CORRESPONDENT, value: object.id.toString()}
+ ]
+ this.router.navigate(["documents"])
+ }
+}
diff --git a/src-ui/src/app/components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component.html b/src-ui/src/app/components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component.html
new file mode 100644
index 000000000..013c5a947
--- /dev/null
+++ b/src-ui/src/app/components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component.html
@@ -0,0 +1,20 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/src-ui/src/app/components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component.scss b/src-ui/src/app/components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src-ui/src/app/components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component.spec.ts b/src-ui/src/app/components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component.spec.ts
new file mode 100644
index 000000000..7d2d7d4a8
--- /dev/null
+++ b/src-ui/src/app/components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DocumentTypeEditDialogComponent } from './document-type-edit-dialog.component';
+
+describe('DocumentTypeEditDialogComponent', () => {
+ let component: DocumentTypeEditDialogComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ DocumentTypeEditDialogComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DocumentTypeEditDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component.ts b/src-ui/src/app/components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component.ts
new file mode 100644
index 000000000..a8052f453
--- /dev/null
+++ b/src-ui/src/app/components/manage/document-type-list/document-type-edit-dialog/document-type-edit-dialog.component.ts
@@ -0,0 +1,29 @@
+import { Component } from '@angular/core';
+import { FormControl, FormGroup } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component';
+import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
+import { DocumentTypeService } from 'src/app/services/rest/document-type.service';
+import { ToastService } from 'src/app/services/toast.service';
+
+@Component({
+ selector: 'app-document-type-edit-dialog',
+ templateUrl: './document-type-edit-dialog.component.html',
+ styleUrls: ['./document-type-edit-dialog.component.scss']
+})
+export class DocumentTypeEditDialogComponent extends EditDialogComponent {
+
+ constructor(service: DocumentTypeService, activeModal: NgbActiveModal, toastService: ToastService) {
+ super(service, activeModal, toastService, 'document type')
+ }
+
+ getForm(): FormGroup {
+ return new FormGroup({
+ name: new FormControl(''),
+ matching_algorithm: new FormControl(1),
+ match: new FormControl(""),
+ is_insensitive: new FormControl(true)
+ })
+ }
+
+}
diff --git a/src-ui/src/app/components/manage/document-type-list/document-type-list.component.html b/src-ui/src/app/components/manage/document-type-list/document-type-list.component.html
new file mode 100644
index 000000000..d2ffab400
--- /dev/null
+++ b/src-ui/src/app/components/manage/document-type-list/document-type-list.component.html
@@ -0,0 +1,51 @@
+
+
+ Create
+
+
+
+
+
+
+
+
+
+
+ Name
+ Matching
+ Document count
+ Actions
+
+
+
+
+ {{ document_type.name }}
+ {{ getMatching(document_type) }}
+ {{ document_type.document_count }}
+
+
+
+
+
+
+ Documents
+
+
+
+
+
+ Edit
+
+
+
+
+
+
+ Delete
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src-ui/src/app/components/manage/document-type-list/document-type-list.component.scss b/src-ui/src/app/components/manage/document-type-list/document-type-list.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src-ui/src/app/components/manage/document-type-list/document-type-list.component.spec.ts b/src-ui/src/app/components/manage/document-type-list/document-type-list.component.spec.ts
new file mode 100644
index 000000000..733860f3b
--- /dev/null
+++ b/src-ui/src/app/components/manage/document-type-list/document-type-list.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DocumentTypeListComponent } from './document-type-list.component';
+
+describe('DocumentTypeListComponent', () => {
+ let component: DocumentTypeListComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ DocumentTypeListComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DocumentTypeListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/manage/document-type-list/document-type-list.component.ts b/src-ui/src/app/components/manage/document-type-list/document-type-list.component.ts
new file mode 100644
index 000000000..d18a19226
--- /dev/null
+++ b/src-ui/src/app/components/manage/document-type-list/document-type-list.component.ts
@@ -0,0 +1,35 @@
+import { Component } from '@angular/core';
+import { Router } from '@angular/router';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { FILTER_DOCUMENT_TYPE } from 'src/app/data/filter-rule-type';
+import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
+import { DocumentListViewService } from 'src/app/services/document-list-view.service';
+import { DocumentTypeService } from 'src/app/services/rest/document-type.service';
+import { GenericListComponent } from '../generic-list/generic-list.component';
+import { DocumentTypeEditDialogComponent } from './document-type-edit-dialog/document-type-edit-dialog.component';
+
+@Component({
+ selector: 'app-document-type-list',
+ templateUrl: './document-type-list.component.html',
+ styleUrls: ['./document-type-list.component.scss']
+})
+export class DocumentTypeListComponent extends GenericListComponent {
+
+ constructor(service: DocumentTypeService, modalService: NgbModal,
+ private router: Router,
+ private list: DocumentListViewService
+ ) {
+ super(service, modalService, DocumentTypeEditDialogComponent)
+ }
+
+ getObjectName(object: PaperlessDocumentType) {
+ return `document type '${object.name}'`
+ }
+
+ filterDocuments(object: PaperlessDocumentType) {
+ this.list.documentListView.filter_rules = [
+ {rule_type: FILTER_DOCUMENT_TYPE, value: object.id.toString()}
+ ]
+ this.router.navigate(["documents"])
+ }
+}
diff --git a/src-ui/src/app/components/manage/generic-list/generic-list.component.spec.ts b/src-ui/src/app/components/manage/generic-list/generic-list.component.spec.ts
new file mode 100644
index 000000000..d13da8dd6
--- /dev/null
+++ b/src-ui/src/app/components/manage/generic-list/generic-list.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { GenericListComponent } from './generic-list.component';
+
+describe('GenericListComponent', () => {
+ let component: GenericListComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ GenericListComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(GenericListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/manage/generic-list/generic-list.component.ts b/src-ui/src/app/components/manage/generic-list/generic-list.component.ts
new file mode 100644
index 000000000..783c22b36
--- /dev/null
+++ b/src-ui/src/app/components/manage/generic-list/generic-list.component.ts
@@ -0,0 +1,106 @@
+import { Directive, OnInit, QueryList, ViewChildren } from '@angular/core';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { MatchingModel, MATCHING_ALGORITHMS, MATCH_AUTO } from 'src/app/data/matching-model';
+import { ObjectWithId } from 'src/app/data/object-with-id';
+import { SortableDirective, SortEvent } from 'src/app/directives/sortable.directive';
+import { AbstractPaperlessService } from 'src/app/services/rest/abstract-paperless-service';
+import { ConfirmDialogComponent } from '../../common/confirm-dialog/confirm-dialog.component';
+
+@Directive()
+export abstract class GenericListComponent implements OnInit {
+
+ constructor(
+ private service: AbstractPaperlessService,
+ private modalService: NgbModal,
+ private editDialogComponent: any) {
+ }
+
+ @ViewChildren(SortableDirective) headers: QueryList;
+
+ public data: T[] = []
+
+ public page = 1
+
+ public collectionSize = 0
+
+ public sortField: string
+ public sortDirection: string
+
+ getMatching(o: MatchingModel) {
+ if (o.matching_algorithm == MATCH_AUTO) {
+ return "Automatic"
+ } else if (o.match && o.match.length > 0) {
+ return `${o.match} (${MATCHING_ALGORITHMS.find(a => a.id == o.matching_algorithm).name})`
+ } else {
+ return "-"
+ }
+ }
+
+ onSort(event: SortEvent) {
+
+ if (event.direction && event.direction.length > 0) {
+ this.sortField = event.column
+ this.sortDirection = event.direction
+ } else {
+ this.sortField = null
+ this.sortDirection = null
+ }
+
+ this.headers.forEach(header => {
+ if (header.sortable !== this.sortField) {
+ header.direction = '';
+ }
+ });
+
+ this.reloadData()
+ }
+
+ ngOnInit(): void {
+ this.reloadData()
+ }
+
+ reloadData() {
+ // TODO: this is a hack
+ this.service.list(this.page, null, this.sortField, this.sortDirection == 'des').subscribe(c => {
+ this.data = c.results
+ this.collectionSize = c.count
+ });
+ }
+
+ openCreateDialog() {
+ var activeModal = this.modalService.open(this.editDialogComponent, {backdrop: 'static'})
+ activeModal.componentInstance.dialogMode = 'create'
+ activeModal.componentInstance.success.subscribe(o => {
+ this.reloadData()
+ })
+ }
+
+ openEditDialog(object: T) {
+ var activeModal = this.modalService.open(this.editDialogComponent, {backdrop: 'static'})
+ activeModal.componentInstance.object = object
+ activeModal.componentInstance.dialogMode = 'edit'
+ activeModal.componentInstance.success.subscribe(o => {
+ this.reloadData()
+ })
+ }
+
+ getObjectName(object: T) {
+ return object.toString()
+ }
+
+ openDeleteDialog(object: T) {
+ var activeModal = this.modalService.open(ConfirmDialogComponent, {backdrop: 'static'})
+ activeModal.componentInstance.title = "Confirm delete"
+ activeModal.componentInstance.messageBold = `Do you really want to delete ${this.getObjectName(object)}?`
+ activeModal.componentInstance.message = "Associated documents will not be deleted."
+ activeModal.componentInstance.btnClass = "btn-danger"
+ activeModal.componentInstance.btnCaption = "Delete"
+ activeModal.componentInstance.confirmClicked.subscribe(() => {
+ this.service.delete(object).subscribe(_ => {
+ activeModal.close()
+ this.reloadData()
+ })
+ }
+ )
+ }
+}
diff --git a/src-ui/src/app/components/manage/logs/logs.component.html b/src-ui/src/app/components/manage/logs/logs.component.html
new file mode 100644
index 000000000..6af482c66
--- /dev/null
+++ b/src-ui/src/app/components/manage/logs/logs.component.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+ Filter
+
+
+ {{f.name}}
+
+
+
+
+
+
+
+ {{log.created | date:'short'}}
+ {{getLevelText(log.level)}}
+ {{log.message}}
+
+
\ No newline at end of file
diff --git a/src-ui/src/app/components/manage/logs/logs.component.scss b/src-ui/src/app/components/manage/logs/logs.component.scss
new file mode 100644
index 000000000..dee9b10dc
--- /dev/null
+++ b/src-ui/src/app/components/manage/logs/logs.component.scss
@@ -0,0 +1,16 @@
+.log-entry-10 {
+ color: lightslategray !important;
+}
+
+.log-entry-30 {
+ color: yellow !important;
+}
+
+.log-entry-40 {
+ color: red !important;
+}
+
+.log-entry-50 {
+ color: lightcoral !important;
+ font-weight: bold;
+}
\ No newline at end of file
diff --git a/src-ui/src/app/components/manage/logs/logs.component.spec.ts b/src-ui/src/app/components/manage/logs/logs.component.spec.ts
new file mode 100644
index 000000000..12a4e107b
--- /dev/null
+++ b/src-ui/src/app/components/manage/logs/logs.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { LogsComponent } from './logs.component';
+
+describe('LogsComponent', () => {
+ let component: LogsComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ LogsComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(LogsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/manage/logs/logs.component.ts b/src-ui/src/app/components/manage/logs/logs.component.ts
new file mode 100644
index 000000000..b131796ee
--- /dev/null
+++ b/src-ui/src/app/components/manage/logs/logs.component.ts
@@ -0,0 +1,48 @@
+import { Component, OnInit } from '@angular/core';
+import { LOG_LEVELS, LOG_LEVEL_INFO, PaperlessLog } from 'src/app/data/paperless-log';
+import { LogService } from 'src/app/services/rest/log.service';
+
+@Component({
+ selector: 'app-logs',
+ templateUrl: './logs.component.html',
+ styleUrls: ['./logs.component.scss']
+})
+export class LogsComponent implements OnInit {
+
+ constructor(private logService: LogService) { }
+
+ logs: PaperlessLog[] = []
+ level: number = LOG_LEVEL_INFO
+
+ ngOnInit(): void {
+ this.reload()
+ }
+
+ reload() {
+ this.logService.list(1, 50, 'created', true, {'level__gte': this.level}).subscribe(result => this.logs = result.results)
+ }
+
+ getLevelText(level: number) {
+ return LOG_LEVELS.find(l => l.id == level)?.name
+ }
+
+ onScroll() {
+ let lastCreated = null
+ if (this.logs.length > 0) {
+ lastCreated = new Date(this.logs[this.logs.length-1].created).toISOString()
+ }
+ this.logService.list(1, 25, 'created', true, {'created__lt': lastCreated, 'level__gte': this.level}).subscribe(result => {
+ this.logs.push(...result.results)
+ })
+ }
+
+ getLevels() {
+ return LOG_LEVELS
+ }
+
+ setLevel(id) {
+ this.level = id
+ this.reload()
+ }
+
+}
diff --git a/src-ui/src/app/components/manage/settings/settings.component.html b/src-ui/src/app/components/manage/settings/settings.component.html
new file mode 100644
index 000000000..f71f12238
--- /dev/null
+++ b/src-ui/src/app/components/manage/settings/settings.component.html
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+ Save
+
\ No newline at end of file
diff --git a/src-ui/src/app/components/manage/settings/settings.component.scss b/src-ui/src/app/components/manage/settings/settings.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src-ui/src/app/components/manage/settings/settings.component.spec.ts b/src-ui/src/app/components/manage/settings/settings.component.spec.ts
new file mode 100644
index 000000000..a3a508b0e
--- /dev/null
+++ b/src-ui/src/app/components/manage/settings/settings.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SettingsComponent } from './settings.component';
+
+describe('SettingsComponent', () => {
+ let component: SettingsComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ SettingsComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(SettingsComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/manage/settings/settings.component.ts b/src-ui/src/app/components/manage/settings/settings.component.ts
new file mode 100644
index 000000000..f839010b1
--- /dev/null
+++ b/src-ui/src/app/components/manage/settings/settings.component.ts
@@ -0,0 +1,75 @@
+import { Component, OnInit } from '@angular/core';
+import { FormControl, FormGroup } from '@angular/forms';
+import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
+import { GENERAL_SETTINGS } from 'src/app/data/storage-keys';
+import { DocumentListViewService } from 'src/app/services/document-list-view.service';
+import { SavedViewService } from 'src/app/services/rest/saved-view.service';
+import { Toast, ToastService } from 'src/app/services/toast.service';
+
+@Component({
+ selector: 'app-settings',
+ templateUrl: './settings.component.html',
+ styleUrls: ['./settings.component.scss']
+})
+export class SettingsComponent implements OnInit {
+
+ savedViewGroup = new FormGroup({})
+
+ settingsForm = new FormGroup({
+ 'documentListItemPerPage': new FormControl(+localStorage.getItem(GENERAL_SETTINGS.DOCUMENT_LIST_SIZE) || GENERAL_SETTINGS.DOCUMENT_LIST_SIZE_DEFAULT),
+ 'savedViews': this.savedViewGroup
+ })
+
+ constructor(
+ public savedViewService: SavedViewService,
+ private documentListViewService: DocumentListViewService,
+ private toastService: ToastService
+ ) { }
+
+ savedViews: PaperlessSavedView[]
+
+ ngOnInit() {
+ this.savedViewService.listAll().subscribe(r => {
+ this.savedViews = r.results
+ for (let view of this.savedViews) {
+ this.savedViewGroup.addControl(view.id.toString(), new FormGroup({
+ "id": new FormControl(view.id),
+ "name": new FormControl(view.name),
+ "show_on_dashboard": new FormControl(view.show_on_dashboard),
+ "show_in_sidebar": new FormControl(view.show_in_sidebar)
+ }))
+ }
+ })
+ }
+
+ deleteSavedView(savedView: PaperlessSavedView) {
+ this.savedViewService.delete(savedView).subscribe(() => {
+ this.savedViewGroup.removeControl(savedView.id.toString())
+ this.savedViews.splice(this.savedViews.indexOf(savedView), 1)
+ this.toastService.showToast(Toast.make("Information", `Saved view "${savedView.name} deleted.`))
+ })
+ }
+
+ private saveLocalSettings() {
+ localStorage.setItem(GENERAL_SETTINGS.DOCUMENT_LIST_SIZE, this.settingsForm.value.documentListItemPerPage)
+ this.documentListViewService.updatePageSize()
+ this.toastService.showToast(Toast.make("Information", "Settings saved successfully."))
+ }
+
+ saveSettings() {
+ let x = []
+ for (let id in this.savedViewGroup.value) {
+ x.push(this.savedViewGroup.value[id])
+ }
+ if (x.length > 0) {
+ this.savedViewService.patchMany(x).subscribe(s => {
+ this.saveLocalSettings()
+ }, error => {
+ this.toastService.showToast(Toast.makeError(`Error while storing settings on server: ${JSON.stringify(error.error)}`))
+ })
+ } else {
+ this.saveLocalSettings()
+ }
+
+ }
+}
diff --git a/src-ui/src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.html b/src-ui/src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.html
new file mode 100644
index 000000000..8048b0c80
--- /dev/null
+++ b/src-ui/src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.html
@@ -0,0 +1,20 @@
+
+
+
+
+
diff --git a/src-ui/src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.scss b/src-ui/src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src-ui/src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.spec.ts b/src-ui/src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.spec.ts
new file mode 100644
index 000000000..378cefdab
--- /dev/null
+++ b/src-ui/src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TagEditDialogComponent } from './tag-edit-dialog.component';
+
+describe('TagEditDialogComponent', () => {
+ let component: TagEditDialogComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ TagEditDialogComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TagEditDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.ts b/src-ui/src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.ts
new file mode 100644
index 000000000..bb0162608
--- /dev/null
+++ b/src-ui/src/app/components/manage/tag-list/tag-edit-dialog/tag-edit-dialog.component.ts
@@ -0,0 +1,39 @@
+import { Component } from '@angular/core';
+import { FormControl, FormGroup } from '@angular/forms';
+import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
+import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component';
+import { TAG_COLOURS, PaperlessTag } from 'src/app/data/paperless-tag';
+import { TagService } from 'src/app/services/rest/tag.service';
+import { ToastService } from 'src/app/services/toast.service';
+
+@Component({
+ selector: 'app-tag-edit-dialog',
+ templateUrl: './tag-edit-dialog.component.html',
+ styleUrls: ['./tag-edit-dialog.component.scss']
+})
+export class TagEditDialogComponent extends EditDialogComponent {
+
+ constructor(service: TagService, activeModal: NgbActiveModal, toastService: ToastService) {
+ super(service, activeModal, toastService, 'tag')
+ }
+
+ getForm(): FormGroup {
+ return new FormGroup({
+ name: new FormControl(''),
+ colour: new FormControl(1),
+ is_inbox_tag: new FormControl(false),
+ matching_algorithm: new FormControl(1),
+ match: new FormControl(""),
+ is_insensitive: new FormControl(true)
+ })
+ }
+
+ getColours() {
+ return TAG_COLOURS
+ }
+
+ getColor(id: number) {
+ return TAG_COLOURS.find(c => c.id == id)
+ }
+
+}
diff --git a/src-ui/src/app/components/manage/tag-list/tag-list.component.html b/src-ui/src/app/components/manage/tag-list/tag-list.component.html
new file mode 100644
index 000000000..bbe2c6dd2
--- /dev/null
+++ b/src-ui/src/app/components/manage/tag-list/tag-list.component.html
@@ -0,0 +1,54 @@
+
+
+ Create
+
+
+
+
+
+
+
+
+
+
+ Name
+ Colour
+ Matching
+ Document count
+ Actions
+
+
+
+
+ {{ tag.name }}
+ {{ getColor(tag.colour).name }}
+ {{ getMatching(tag) }}
+ {{ tag.document_count }}
+
+
+
+
+
+
+ Documents
+
+
+
+
+
+ Edit
+
+
+
+
+
+
+ Delete
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src-ui/src/app/components/manage/tag-list/tag-list.component.scss b/src-ui/src/app/components/manage/tag-list/tag-list.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src-ui/src/app/components/manage/tag-list/tag-list.component.spec.ts b/src-ui/src/app/components/manage/tag-list/tag-list.component.spec.ts
new file mode 100644
index 000000000..0623f541a
--- /dev/null
+++ b/src-ui/src/app/components/manage/tag-list/tag-list.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TagListComponent } from './tag-list.component';
+
+describe('TagListComponent', () => {
+ let component: TagListComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ TagListComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TagListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/manage/tag-list/tag-list.component.ts b/src-ui/src/app/components/manage/tag-list/tag-list.component.ts
new file mode 100644
index 000000000..e3f151550
--- /dev/null
+++ b/src-ui/src/app/components/manage/tag-list/tag-list.component.ts
@@ -0,0 +1,39 @@
+import { Component } from '@angular/core';
+import { Router } from '@angular/router';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { FILTER_HAS_TAG } from 'src/app/data/filter-rule-type';
+import { TAG_COLOURS, PaperlessTag } from 'src/app/data/paperless-tag';
+import { DocumentListViewService } from 'src/app/services/document-list-view.service';
+import { TagService } from 'src/app/services/rest/tag.service';
+import { GenericListComponent } from '../generic-list/generic-list.component';
+import { TagEditDialogComponent } from './tag-edit-dialog/tag-edit-dialog.component';
+
+@Component({
+ selector: 'app-tag-list',
+ templateUrl: './tag-list.component.html',
+ styleUrls: ['./tag-list.component.scss']
+})
+export class TagListComponent extends GenericListComponent {
+
+ constructor(tagService: TagService, modalService: NgbModal,
+ private router: Router,
+ private list: DocumentListViewService
+ ) {
+ super(tagService, modalService, TagEditDialogComponent)
+ }
+
+ getColor(id) {
+ return TAG_COLOURS.find(c => c.id == id)
+ }
+
+ getObjectName(object: PaperlessTag) {
+ return `tag '${object.name}'`
+ }
+
+ filterDocuments(object: PaperlessTag) {
+ this.list.documentListView.filter_rules = [
+ {rule_type: FILTER_HAS_TAG, value: object.id.toString()}
+ ]
+ this.router.navigate(["documents"])
+ }
+}
diff --git a/src-ui/src/app/components/not-found/not-found.component.html b/src-ui/src/app/components/not-found/not-found.component.html
new file mode 100644
index 000000000..6b21cf3ba
--- /dev/null
+++ b/src-ui/src/app/components/not-found/not-found.component.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
404 Not Found
+
\ No newline at end of file
diff --git a/src-ui/src/app/components/not-found/not-found.component.scss b/src-ui/src/app/components/not-found/not-found.component.scss
new file mode 100644
index 000000000..e69de29bb
diff --git a/src-ui/src/app/components/not-found/not-found.component.spec.ts b/src-ui/src/app/components/not-found/not-found.component.spec.ts
new file mode 100644
index 000000000..9d41c99a6
--- /dev/null
+++ b/src-ui/src/app/components/not-found/not-found.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { NotFoundComponent } from './not-found.component';
+
+describe('NotFoundComponent', () => {
+ let component: NotFoundComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ NotFoundComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(NotFoundComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/not-found/not-found.component.ts b/src-ui/src/app/components/not-found/not-found.component.ts
new file mode 100644
index 000000000..7cb4124f1
--- /dev/null
+++ b/src-ui/src/app/components/not-found/not-found.component.ts
@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'app-not-found',
+ templateUrl: './not-found.component.html',
+ styleUrls: ['./not-found.component.scss']
+})
+export class NotFoundComponent implements OnInit {
+
+ constructor() { }
+
+ ngOnInit(): void {
+ }
+
+}
diff --git a/src-ui/src/app/components/search/result-highlight/result-highlight.component.html b/src-ui/src/app/components/search/result-highlight/result-highlight.component.html
new file mode 100644
index 000000000..1842f5cea
--- /dev/null
+++ b/src-ui/src/app/components/search/result-highlight/result-highlight.component.html
@@ -0,0 +1,3 @@
+...
+ {{token.text}} ...
+
\ No newline at end of file
diff --git a/src-ui/src/app/components/search/result-highlight/result-highlight.component.scss b/src-ui/src/app/components/search/result-highlight/result-highlight.component.scss
new file mode 100644
index 000000000..645fb0426
--- /dev/null
+++ b/src-ui/src/app/components/search/result-highlight/result-highlight.component.scss
@@ -0,0 +1,4 @@
+.match {
+ color: black;
+ background-color: orange;
+}
\ No newline at end of file
diff --git a/src-ui/src/app/components/search/result-highlight/result-highlight.component.spec.ts b/src-ui/src/app/components/search/result-highlight/result-highlight.component.spec.ts
new file mode 100644
index 000000000..8e00a9d0b
--- /dev/null
+++ b/src-ui/src/app/components/search/result-highlight/result-highlight.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ResultHighlightComponent } from './result-highlight.component';
+
+describe('ResultHighlightComponent', () => {
+ let component: ResultHighlightComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ ResultHighlightComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ResultHighlightComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/search/result-highlight/result-highlight.component.ts b/src-ui/src/app/components/search/result-highlight/result-highlight.component.ts
new file mode 100644
index 000000000..d9a1a50b1
--- /dev/null
+++ b/src-ui/src/app/components/search/result-highlight/result-highlight.component.ts
@@ -0,0 +1,19 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { SearchHitHighlight } from 'src/app/data/search-result';
+
+@Component({
+ selector: 'app-result-highlight',
+ templateUrl: './result-highlight.component.html',
+ styleUrls: ['./result-highlight.component.scss']
+})
+export class ResultHighlightComponent implements OnInit {
+
+ constructor() { }
+
+ @Input()
+ highlights: SearchHitHighlight[][]
+
+ ngOnInit(): void {
+ }
+
+}
diff --git a/src-ui/src/app/components/search/search.component.html b/src-ui/src/app/components/search/search.component.html
new file mode 100644
index 000000000..55fcee900
--- /dev/null
+++ b/src-ui/src/app/components/search/search.component.html
@@ -0,0 +1,21 @@
+
+
+
+Invalid search query: {{errorMessage}}
+
+
+ Search string: {{query}}
+
+ - Did you mean "{{correctedQuery}} "?
+
+
+
+
+
+
{{resultCount}} result(s)
+
+
+
+
diff --git a/src-ui/src/app/components/search/search.component.scss b/src-ui/src/app/components/search/search.component.scss
new file mode 100644
index 000000000..40ca79a61
--- /dev/null
+++ b/src-ui/src/app/components/search/search.component.scss
@@ -0,0 +1,15 @@
+.result-content {
+ color: darkgray;
+}
+
+.doc-img {
+ object-fit: cover;
+ object-position: top;
+ height: 100%;
+ position: absolute;
+
+}
+
+.result-content-searching {
+ opacity: 0.3;
+}
\ No newline at end of file
diff --git a/src-ui/src/app/components/search/search.component.spec.ts b/src-ui/src/app/components/search/search.component.spec.ts
new file mode 100644
index 000000000..918ce7071
--- /dev/null
+++ b/src-ui/src/app/components/search/search.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { SearchComponent } from './search.component';
+
+describe('SearchComponent', () => {
+ let component: SearchComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [ SearchComponent ]
+ })
+ .compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(SearchComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/components/search/search.component.ts b/src-ui/src/app/components/search/search.component.ts
new file mode 100644
index 000000000..de8b4652f
--- /dev/null
+++ b/src-ui/src/app/components/search/search.component.ts
@@ -0,0 +1,74 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { SearchHit } from 'src/app/data/search-result';
+import { SearchService } from 'src/app/services/rest/search.service';
+
+@Component({
+ selector: 'app-search',
+ templateUrl: './search.component.html',
+ styleUrls: ['./search.component.scss']
+})
+export class SearchComponent implements OnInit {
+
+ results: SearchHit[] = []
+
+ query: string = ""
+
+ searching = false
+
+ currentPage = 1
+
+ pageCount = 1
+
+ resultCount
+
+ correctedQuery: string = null
+
+ errorMessage: string
+
+ constructor(private searchService: SearchService, private route: ActivatedRoute, private router: Router) { }
+
+ ngOnInit(): void {
+ this.route.queryParamMap.subscribe(paramMap => {
+ this.query = paramMap.get('query')
+ this.searching = true
+ this.currentPage = 1
+ this.loadPage()
+ })
+
+ }
+
+ searchCorrectedQuery() {
+ this.router.navigate(["search"], {queryParams: {query: this.correctedQuery}})
+ }
+
+ loadPage(append: boolean = false) {
+ this.errorMessage = null
+ this.correctedQuery = null
+ this.searchService.search(this.query, this.currentPage).subscribe(result => {
+ if (append) {
+ this.results.push(...result.results)
+ } else {
+ this.results = result.results
+ }
+ this.pageCount = result.page_count
+ this.searching = false
+ this.resultCount = result.count
+ this.correctedQuery = result.corrected_query
+ }, error => {
+ this.searching = false
+ this.resultCount = 1
+ this.pageCount = 1
+ this.results = []
+ this.errorMessage = error.error
+ })
+ }
+
+ onScroll() {
+ if (this.currentPage < this.pageCount) {
+ this.currentPage += 1
+ this.loadPage(true)
+ }
+ }
+
+}
diff --git a/src-ui/src/app/data/filter-rule-type.ts b/src-ui/src/app/data/filter-rule-type.ts
new file mode 100644
index 000000000..ea8e60eee
--- /dev/null
+++ b/src-ui/src/app/data/filter-rule-type.ts
@@ -0,0 +1,57 @@
+export const FILTER_TITLE = 0
+export const FILTER_CONTENT = 1
+export const FILTER_ASN = 2
+export const FILTER_CORRESPONDENT = 3
+export const FILTER_DOCUMENT_TYPE = 4
+export const FILTER_IS_IN_INBOX = 5
+export const FILTER_HAS_TAG = 6
+export const FILTER_HAS_ANY_TAG = 7
+export const FILTER_CREATED_BEFORE = 8
+export const FILTER_CREATED_AFTER = 9
+export const FILTER_CREATED_YEAR = 10
+export const FILTER_CREATED_MONTH = 11
+export const FILTER_CREATED_DAY = 12
+export const FILTER_ADDED_BEFORE = 13
+export const FILTER_ADDED_AFTER = 14
+export const FILTER_MODIFIED_BEFORE = 15
+export const FILTER_MODIFIED_AFTER = 16
+
+export const FILTER_DOES_NOT_HAVE_TAG = 17
+
+export const FILTER_RULE_TYPES: FilterRuleType[] = [
+
+ {id: FILTER_TITLE, name: "Title contains", filtervar: "title__icontains", datatype: "string", multi: false, default: ""},
+ {id: FILTER_CONTENT, name: "Content contains", filtervar: "content__icontains", datatype: "string", multi: false, default: ""},
+
+ {id: FILTER_ASN, name: "ASN is", filtervar: "archive_serial_number", datatype: "number", multi: false},
+
+ {id: FILTER_CORRESPONDENT, name: "Correspondent is", filtervar: "correspondent__id", datatype: "correspondent", multi: false},
+ {id: FILTER_DOCUMENT_TYPE, name: "Document type is", filtervar: "document_type__id", datatype: "document_type", multi: false},
+
+ {id: FILTER_IS_IN_INBOX, name: "Is in Inbox", filtervar: "is_in_inbox", datatype: "boolean", multi: false, default: true},
+ {id: FILTER_HAS_TAG, name: "Has tag", filtervar: "tags__id__all", datatype: "tag", multi: true},
+ {id: FILTER_DOES_NOT_HAVE_TAG, name: "Does not have tag", filtervar: "tags__id__none", datatype: "tag", multi: true},
+ {id: FILTER_HAS_ANY_TAG, name: "Has any tag", filtervar: "is_tagged", datatype: "boolean", multi: false, default: true},
+
+ {id: FILTER_CREATED_BEFORE, name: "Created before", filtervar: "created__date__lt", datatype: "date", multi: false},
+ {id: FILTER_CREATED_AFTER, name: "Created after", filtervar: "created__date__gt", datatype: "date", multi: false},
+
+ {id: FILTER_CREATED_YEAR, name: "Year created is", filtervar: "created__year", datatype: "number", multi: false},
+ {id: FILTER_CREATED_MONTH, name: "Month created is", filtervar: "created__month", datatype: "number", multi: false},
+ {id: FILTER_CREATED_DAY, name: "Day created is", filtervar: "created__day", datatype: "number", multi: false},
+
+ {id: FILTER_ADDED_BEFORE, name: "Added before", filtervar: "added__date__lt", datatype: "date", multi: false},
+ {id: FILTER_ADDED_AFTER, name: "Added after", filtervar: "added__date__gt", datatype: "date", multi: false},
+
+ {id: FILTER_MODIFIED_BEFORE, name: "Modified before", filtervar: "modified__date__lt", datatype: "date", multi: false},
+ {id: FILTER_MODIFIED_AFTER, name: "Modified after", filtervar: "modified__date__gt", datatype: "date", multi: false},
+]
+
+export interface FilterRuleType {
+ id: number
+ name: string
+ filtervar: string
+ datatype: string //number, string, boolean, date
+ multi: boolean
+ default?: any
+}
diff --git a/src-ui/src/app/data/filter-rule.ts b/src-ui/src/app/data/filter-rule.ts
new file mode 100644
index 000000000..82d8498f3
--- /dev/null
+++ b/src-ui/src/app/data/filter-rule.ts
@@ -0,0 +1,16 @@
+export function cloneFilterRules(filterRules: FilterRule[]): FilterRule[] {
+ if (filterRules) {
+ let newRules: FilterRule[] = []
+ for (let rule of filterRules) {
+ newRules.push({rule_type: rule.rule_type, value: rule.value})
+ }
+ return newRules
+ } else {
+ return null
+ }
+}
+
+export interface FilterRule {
+ rule_type: number
+ value: string
+}
\ No newline at end of file
diff --git a/src-ui/src/app/data/matching-model.ts b/src-ui/src/app/data/matching-model.ts
new file mode 100644
index 000000000..698c32da5
--- /dev/null
+++ b/src-ui/src/app/data/matching-model.ts
@@ -0,0 +1,32 @@
+import { ObjectWithId } from './object-with-id';
+
+
+export const MATCH_ANY = 1
+export const MATCH_ALL = 2
+export const MATCH_LITERAL = 3
+export const MATCH_REGEX = 4
+export const MATCH_FUZZY = 5
+export const MATCH_AUTO = 6
+
+export const MATCHING_ALGORITHMS = [
+ {id: MATCH_ANY, name: "Any"},
+ {id: MATCH_ALL, name: "All"},
+ {id: MATCH_LITERAL, name: "Literal"},
+ {id: MATCH_REGEX, name: "Regular Expression"},
+ {id: MATCH_FUZZY, name: "Fuzzy Match"},
+ {id: MATCH_AUTO, name: "Auto"},
+]
+
+export interface MatchingModel extends ObjectWithId {
+
+ name?: string
+
+ slug?: string
+
+ match?: string
+
+ matching_algorithm?: number
+
+ is_insensitive?: boolean
+
+}
diff --git a/src-ui/src/app/data/object-with-id.ts b/src-ui/src/app/data/object-with-id.ts
new file mode 100644
index 000000000..e81548f4e
--- /dev/null
+++ b/src-ui/src/app/data/object-with-id.ts
@@ -0,0 +1,5 @@
+export interface ObjectWithId {
+
+ id?: number
+
+}
diff --git a/src-ui/src/app/data/paperless-correspondent.ts b/src-ui/src/app/data/paperless-correspondent.ts
new file mode 100644
index 000000000..217e62529
--- /dev/null
+++ b/src-ui/src/app/data/paperless-correspondent.ts
@@ -0,0 +1,9 @@
+import { MatchingModel } from './matching-model';
+
+export interface PaperlessCorrespondent extends MatchingModel {
+
+ document_count?: number
+
+ last_correspondence?: Date
+
+}
diff --git a/src-ui/src/app/data/paperless-document-metadata.ts b/src-ui/src/app/data/paperless-document-metadata.ts
new file mode 100644
index 000000000..12f0a78d8
--- /dev/null
+++ b/src-ui/src/app/data/paperless-document-metadata.ts
@@ -0,0 +1,13 @@
+export interface PaperlessDocumentMetadata {
+
+ original_checksum?: string
+
+ archived_checksum?: string
+
+ original_mime_type?: string
+
+ media_filename?: string
+
+ has_archive_version?: boolean
+
+}
\ No newline at end of file
diff --git a/src-ui/src/app/data/paperless-document-type.ts b/src-ui/src/app/data/paperless-document-type.ts
new file mode 100644
index 000000000..d099bec47
--- /dev/null
+++ b/src-ui/src/app/data/paperless-document-type.ts
@@ -0,0 +1,7 @@
+import { MatchingModel } from './matching-model';
+
+export interface PaperlessDocumentType extends MatchingModel {
+
+ document_count?: number
+
+}
diff --git a/src-ui/src/app/data/paperless-document.ts b/src-ui/src/app/data/paperless-document.ts
new file mode 100644
index 000000000..9d0aeda88
--- /dev/null
+++ b/src-ui/src/app/data/paperless-document.ts
@@ -0,0 +1,43 @@
+import { PaperlessCorrespondent } from './paperless-correspondent'
+import { ObjectWithId } from './object-with-id'
+import { PaperlessTag } from './paperless-tag'
+import { PaperlessDocumentType } from './paperless-document-type'
+import { Observable } from 'rxjs'
+
+export interface PaperlessDocument extends ObjectWithId {
+
+ correspondent$?: Observable
+
+ correspondent?: number
+
+ document_type$?: Observable
+
+ document_type?: number
+
+ title?: string
+
+ content?: string
+
+ file_type?: string
+
+ tags$?: Observable
+
+ tags?: number[]
+
+ checksum?: string
+
+ created?: Date
+
+ modified?: Date
+
+ added?: Date
+
+ file_name?: string
+
+ download_url?: string
+
+ thumbnail_url?: string
+
+ archive_serial_number?: number
+
+}
diff --git a/src-ui/src/app/data/paperless-log.ts b/src-ui/src/app/data/paperless-log.ts
new file mode 100644
index 000000000..61a6fce99
--- /dev/null
+++ b/src-ui/src/app/data/paperless-log.ts
@@ -0,0 +1,27 @@
+export const LOG_LEVEL_DEBUG = 10
+export const LOG_LEVEL_INFO = 20
+export const LOG_LEVEL_WARNING = 30
+export const LOG_LEVEL_ERROR = 40
+export const LOG_LEVEL_CRITICAL = 50
+
+export const LOG_LEVELS = [
+ {id: LOG_LEVEL_DEBUG, name: "DEBUG"},
+ {id: LOG_LEVEL_INFO, name: "INFO"},
+ {id: LOG_LEVEL_WARNING, name: "WARNING"},
+ {id: LOG_LEVEL_ERROR, name: "ERROR"},
+ {id: LOG_LEVEL_CRITICAL, name: "CRITICAL"}
+]
+
+export interface PaperlessLog {
+
+ id?: number
+
+ group?: string
+
+ message?: string
+
+ created?: Date
+
+ level?: number
+
+}
diff --git a/src-ui/src/app/data/paperless-saved-view.ts b/src-ui/src/app/data/paperless-saved-view.ts
new file mode 100644
index 000000000..fbc2f5d5e
--- /dev/null
+++ b/src-ui/src/app/data/paperless-saved-view.ts
@@ -0,0 +1,18 @@
+import { FilterRule } from './filter-rule';
+import { ObjectWithId } from './object-with-id';
+
+export interface PaperlessSavedView extends ObjectWithId {
+
+ name?: string
+
+ show_on_dashboard?: boolean
+
+ show_in_sidebar?: boolean
+
+ sort_field: string
+
+ sort_reverse: boolean
+
+ filter_rules: FilterRule[]
+
+}
\ No newline at end of file
diff --git a/src-ui/src/app/data/paperless-tag.ts b/src-ui/src/app/data/paperless-tag.ts
new file mode 100644
index 000000000..551c6e03a
--- /dev/null
+++ b/src-ui/src/app/data/paperless-tag.ts
@@ -0,0 +1,28 @@
+import { MatchingModel } from './matching-model';
+import { ObjectWithId } from './object-with-id';
+
+
+export const TAG_COLOURS = [
+ {id: 1, value: "#a6cee3", name: "Light Blue", textColor: "#000000"},
+ {id: 2, value: "#1f78b4", name: "Blue", textColor: "#ffffff"},
+ {id: 3, value: "#b2df8a", name: "Light Green", textColor: "#000000"},
+ {id: 4, value: "#33a02c", name: "Green", textColor: "#000000"},
+ {id: 5, value: "#fb9a99", name: "Light Red", textColor: "#000000"},
+ {id: 6, value: "#e31a1c", name: "Red ", textColor: "#ffffff"},
+ {id: 7, value: "#fdbf6f", name: "Light Orange", textColor: "#000000"},
+ {id: 8, value: "#ff7f00", name: "Orange", textColor: "#000000"},
+ {id: 9, value: "#cab2d6", name: "Light Violet", textColor: "#000000"},
+ {id: 10, value: "#6a3d9a", name: "Violet", textColor: "#ffffff"},
+ {id: 11, value: "#b15928", name: "Brown", textColor: "#000000"},
+ {id: 12, value: "#000000", name: "Black", textColor: "#ffffff"},
+ {id: 13, value: "#cccccc", name: "Light Grey", textColor: "#000000"}
+]
+
+export interface PaperlessTag extends MatchingModel {
+
+ colour?: number
+
+ is_inbox_tag?: boolean
+
+ document_count?: number
+}
diff --git a/src-ui/src/app/data/results.ts b/src-ui/src/app/data/results.ts
new file mode 100644
index 000000000..83e9c583c
--- /dev/null
+++ b/src-ui/src/app/data/results.ts
@@ -0,0 +1,7 @@
+export interface Results {
+
+ count: number
+
+ results: T[]
+
+}
diff --git a/src-ui/src/app/data/search-result.ts b/src-ui/src/app/data/search-result.ts
new file mode 100644
index 000000000..a769a8351
--- /dev/null
+++ b/src-ui/src/app/data/search-result.ts
@@ -0,0 +1,29 @@
+import { PaperlessDocument } from './paperless-document'
+
+export class SearchHitHighlight {
+ text?: string
+ term?: number
+}
+
+export interface SearchHit {
+ id?: number
+ title?: string
+ score?: number
+ rank?: number
+
+ highlights?: SearchHitHighlight[][]
+ document?: PaperlessDocument
+}
+
+export interface SearchResult {
+
+ count?: number
+ page?: number
+ page_count?: number
+
+ corrected_query?: string
+
+ results?: SearchHit[]
+
+
+}
diff --git a/src-ui/src/app/data/storage-keys.ts b/src-ui/src/app/data/storage-keys.ts
new file mode 100644
index 000000000..13b41d4a7
--- /dev/null
+++ b/src-ui/src/app/data/storage-keys.ts
@@ -0,0 +1,12 @@
+export const OPEN_DOCUMENT_SERVICE = {
+ DOCUMENTS: 'open-documents-service:openDocuments'
+}
+
+export const DOCUMENT_LIST_SERVICE = {
+ CURRENT_VIEW_CONFIG: 'document-list-service:currentViewConfig'
+}
+
+export const GENERAL_SETTINGS = {
+ DOCUMENT_LIST_SIZE: 'general-settings:documentListSize',
+ DOCUMENT_LIST_SIZE_DEFAULT: 50
+}
\ No newline at end of file
diff --git a/src-ui/src/app/directives/sortable.directive.spec.ts b/src-ui/src/app/directives/sortable.directive.spec.ts
new file mode 100644
index 000000000..f77b499de
--- /dev/null
+++ b/src-ui/src/app/directives/sortable.directive.spec.ts
@@ -0,0 +1,8 @@
+import { SortableDirective } from './sortable.directive';
+
+describe('SortableDirective', () => {
+ it('should create an instance', () => {
+ const directive = new SortableDirective();
+ expect(directive).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/directives/sortable.directive.ts b/src-ui/src/app/directives/sortable.directive.ts
new file mode 100644
index 000000000..11c474dbb
--- /dev/null
+++ b/src-ui/src/app/directives/sortable.directive.ts
@@ -0,0 +1,30 @@
+import { Directive, EventEmitter, Input, Output } from '@angular/core';
+
+export interface SortEvent {
+ column: string;
+ direction: string;
+}
+
+const rotate: {[key: string]: string} = { 'asc': 'des', 'des': '', '': 'asc' };
+
+@Directive({
+ selector: 'th[sortable]',
+ host: {
+ '[class.asc]': 'direction === "asc"',
+ '[class.des]': 'direction === "des"',
+ '(click)': 'rotate()'
+ }
+})
+export class SortableDirective {
+
+ constructor() { }
+
+ @Input() sortable: string = '';
+ @Input() direction: string = '';
+ @Output() sort = new EventEmitter();
+
+ rotate() {
+ this.direction = rotate[this.direction];
+ this.sort.emit({column: this.sortable, direction: this.direction});
+ }
+}
diff --git a/src-ui/src/app/interceptors/csrf.interceptor.spec.ts b/src-ui/src/app/interceptors/csrf.interceptor.spec.ts
new file mode 100644
index 000000000..64e20c110
--- /dev/null
+++ b/src-ui/src/app/interceptors/csrf.interceptor.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { CsrfInterceptor } from './csrf.interceptor';
+
+describe('CsrfInterceptor', () => {
+ beforeEach(() => TestBed.configureTestingModule({
+ providers: [
+ CsrfInterceptor
+ ]
+ }));
+
+ it('should be created', () => {
+ const interceptor: CsrfInterceptor = TestBed.inject(CsrfInterceptor);
+ expect(interceptor).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/interceptors/csrf.interceptor.ts b/src-ui/src/app/interceptors/csrf.interceptor.ts
new file mode 100644
index 000000000..32f3e99dc
--- /dev/null
+++ b/src-ui/src/app/interceptors/csrf.interceptor.ts
@@ -0,0 +1,30 @@
+import { Injectable } from '@angular/core';
+import {
+ HttpRequest,
+ HttpHandler,
+ HttpEvent,
+ HttpInterceptor
+} from '@angular/common/http';
+import { Observable } from 'rxjs';
+import { CookieService } from 'ngx-cookie-service';
+
+@Injectable()
+export class CsrfInterceptor implements HttpInterceptor {
+
+ constructor(private cookieService: CookieService) {
+
+ }
+
+ intercept(request: HttpRequest, next: HttpHandler): Observable> {
+ let csrfToken = this.cookieService.get('csrftoken')
+ if (csrfToken) {
+ request = request.clone({
+ setHeaders: {
+ 'X-CSRFToken': csrfToken
+ }
+ })
+ }
+
+ return next.handle(request);
+ }
+}
diff --git a/src-ui/src/app/pipes/document-title.pipe.spec.ts b/src-ui/src/app/pipes/document-title.pipe.spec.ts
new file mode 100644
index 000000000..29835abd6
--- /dev/null
+++ b/src-ui/src/app/pipes/document-title.pipe.spec.ts
@@ -0,0 +1,8 @@
+import { DocumentTitlePipe } from './document-title.pipe';
+
+describe('DocumentTitlePipe', () => {
+ it('create an instance', () => {
+ const pipe = new DocumentTitlePipe();
+ expect(pipe).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/pipes/document-title.pipe.ts b/src-ui/src/app/pipes/document-title.pipe.ts
new file mode 100644
index 000000000..621562d39
--- /dev/null
+++ b/src-ui/src/app/pipes/document-title.pipe.ts
@@ -0,0 +1,16 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'documentTitle'
+})
+export class DocumentTitlePipe implements PipeTransform {
+
+ transform(value: string): string {
+ if (value) {
+ return value
+ } else {
+ return "(no title)"
+ }
+ }
+
+}
diff --git a/src-ui/src/app/pipes/file-size.pipe.spec.ts b/src-ui/src/app/pipes/file-size.pipe.spec.ts
new file mode 100644
index 000000000..8c7a39d22
--- /dev/null
+++ b/src-ui/src/app/pipes/file-size.pipe.spec.ts
@@ -0,0 +1,8 @@
+import { FileSizePipe } from './file-size.pipe';
+
+describe('FileSizePipe', () => {
+ it('create an instance', () => {
+ const pipe = new FileSizePipe();
+ expect(pipe).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/pipes/file-size.pipe.ts b/src-ui/src/app/pipes/file-size.pipe.ts
new file mode 100644
index 000000000..7d742c876
--- /dev/null
+++ b/src-ui/src/app/pipes/file-size.pipe.ts
@@ -0,0 +1,77 @@
+/**
+ * https://gist.github.com/JonCatmull/ecdf9441aaa37336d9ae2c7f9cb7289a
+ *
+ * @license
+ * Copyright (c) 2019 Jonathan Catmull.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+import { Pipe, PipeTransform } from '@angular/core';
+
+type unit = 'bytes' | 'KB' | 'MB' | 'GB' | 'TB' | 'PB';
+type unitPrecisionMap = {
+ [u in unit]: number;
+};
+
+const defaultPrecisionMap: unitPrecisionMap = {
+ bytes: 0,
+ KB: 0,
+ MB: 1,
+ GB: 1,
+ TB: 2,
+ PB: 2
+};
+
+/*
+ * Convert bytes into largest possible unit.
+ * Takes an precision argument that can be a number or a map for each unit.
+ * Usage:
+ * bytes | fileSize:precision
+ * @example
+ * // returns 1 KB
+ * {{ 1500 | fileSize }}
+ * @example
+ * // returns 2.1 GB
+ * {{ 2100000000 | fileSize }}
+ * @example
+ * // returns 1.46 KB
+ * {{ 1500 | fileSize:2 }}
+ */
+@Pipe({ name: 'fileSize' })
+export class FileSizePipe implements PipeTransform {
+ private readonly units: unit[] = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
+
+ transform(bytes: number = 0, precision: number | unitPrecisionMap = defaultPrecisionMap): string {
+ if (isNaN(parseFloat(String(bytes))) || !isFinite(bytes)) return '?';
+
+ let unitIndex = 0;
+
+ while (bytes >= 1024) {
+ bytes /= 1024;
+ unitIndex++;
+ }
+
+ const unit = this.units[unitIndex];
+
+ if (typeof precision === 'number') {
+ return `${bytes.toFixed(+precision)} ${unit}`;
+ }
+ return `${bytes.toFixed(precision[unit])} ${unit}`;
+ }
+}
diff --git a/src-ui/src/app/pipes/filter.pipe.ts b/src-ui/src/app/pipes/filter.pipe.ts
new file mode 100644
index 000000000..f799f40cc
--- /dev/null
+++ b/src-ui/src/app/pipes/filter.pipe.ts
@@ -0,0 +1,17 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'filter'
+})
+export class FilterPipe implements PipeTransform {
+ transform(items: any[], searchText: string): any[] {
+ if (!items) return [];
+ if (!searchText) return items;
+
+ return items.filter(item => {
+ return Object.keys(item).some(key => {
+ return String(item[key]).toLowerCase().includes(searchText.toLowerCase());
+ });
+ });
+ }
+}
diff --git a/src-ui/src/app/pipes/yes-no.pipe.spec.ts b/src-ui/src/app/pipes/yes-no.pipe.spec.ts
new file mode 100644
index 000000000..80acd8acd
--- /dev/null
+++ b/src-ui/src/app/pipes/yes-no.pipe.spec.ts
@@ -0,0 +1,8 @@
+import { YesNoPipe } from './yes-no.pipe';
+
+describe('YesNoPipe', () => {
+ it('create an instance', () => {
+ const pipe = new YesNoPipe();
+ expect(pipe).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/pipes/yes-no.pipe.ts b/src-ui/src/app/pipes/yes-no.pipe.ts
new file mode 100644
index 000000000..9a4ed56ef
--- /dev/null
+++ b/src-ui/src/app/pipes/yes-no.pipe.ts
@@ -0,0 +1,12 @@
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({
+ name: 'yesno'
+})
+export class YesNoPipe implements PipeTransform {
+
+ transform(value: boolean): unknown {
+ return value ? "Yes" : "No"
+ }
+
+}
diff --git a/src-ui/src/app/services/document-list-view.service.spec.ts b/src-ui/src/app/services/document-list-view.service.spec.ts
new file mode 100644
index 000000000..92a1177aa
--- /dev/null
+++ b/src-ui/src/app/services/document-list-view.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { DocumentListViewService } from './document-list-view.service';
+
+describe('DocumentListViewService', () => {
+ let service: DocumentListViewService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(DocumentListViewService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/services/document-list-view.service.ts b/src-ui/src/app/services/document-list-view.service.ts
new file mode 100644
index 000000000..57d0a3f0e
--- /dev/null
+++ b/src-ui/src/app/services/document-list-view.service.ts
@@ -0,0 +1,212 @@
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs';
+import { cloneFilterRules, FilterRule } from '../data/filter-rule';
+import { PaperlessDocument } from '../data/paperless-document';
+import { PaperlessSavedView } from '../data/paperless-saved-view';
+import { DOCUMENT_LIST_SERVICE, GENERAL_SETTINGS } from '../data/storage-keys';
+import { DocumentService } from './rest/document.service';
+
+
+/**
+ * This service manages the document list which is displayed using the document list view.
+ *
+ * This service also serves saved views by transparently switching between the document list
+ * and saved views on request. See below.
+ */
+@Injectable({
+ providedIn: 'root'
+})
+export class DocumentListViewService {
+
+ static DEFAULT_SORT_FIELD = 'created'
+
+ isReloading: boolean = false
+ documents: PaperlessDocument[] = []
+ currentPage = 1
+ currentPageSize: number = +localStorage.getItem(GENERAL_SETTINGS.DOCUMENT_LIST_SIZE) || GENERAL_SETTINGS.DOCUMENT_LIST_SIZE_DEFAULT
+ collectionSize: number
+
+ /**
+ * This is the current config for the document list. The service will always remember the last settings used for the document list.
+ */
+ private _documentListViewConfig: PaperlessSavedView
+ /**
+ * Optionally, this is the currently selected saved view, which might be null.
+ */
+ private _savedViewConfig: PaperlessSavedView
+
+ get savedView(): PaperlessSavedView {
+ return this._savedViewConfig
+ }
+
+ set savedView(value: PaperlessSavedView) {
+ if (value) {
+ //this is here so that we don't modify value, which might be the actual instance of the saved view.
+ this._savedViewConfig = Object.assign({}, value)
+ } else {
+ this._savedViewConfig = null
+ }
+ }
+
+ get savedViewId() {
+ return this.savedView?.id
+ }
+
+ get savedViewTitle() {
+ return this.savedView?.name
+ }
+
+ get documentListView() {
+ return this._documentListViewConfig
+ }
+
+ set documentListView(value) {
+ if (value) {
+ this._documentListViewConfig = Object.assign({}, value)
+ this.saveDocumentListView()
+ }
+ }
+
+ /**
+ * This is what switches between the saved views and the document list view. Everything on the document list uses
+ * this property to determine the settings for the currently displayed document list.
+ */
+ get view() {
+ return this.savedView || this.documentListView
+ }
+
+ load(view: PaperlessSavedView) {
+ this.documentListView.filter_rules = cloneFilterRules(view.filter_rules)
+ this.documentListView.sort_reverse = view.sort_reverse
+ this.documentListView.sort_field = view.sort_field
+ this.saveDocumentListView()
+ }
+
+ clear() {
+ this.collectionSize = null
+ this.documents = []
+ this.currentPage = 1
+ }
+
+ reload(onFinish?) {
+ this.isReloading = true
+ this.documentService.list(
+ this.currentPage,
+ this.currentPageSize,
+ this.view.sort_field,
+ this.view.sort_reverse,
+ this.view.filter_rules).subscribe(
+ result => {
+ this.collectionSize = result.count
+ this.documents = result.results
+ if (onFinish) {
+ onFinish()
+ }
+ this.isReloading = false
+ },
+ error => {
+ if (error.error['detail'] == 'Invalid page.') {
+ this.currentPage = 1
+ this.reload()
+ }
+ this.isReloading = false
+ })
+ }
+
+ set filterRules(filterRules: FilterRule[]) {
+ //we're going to clone the filterRules object, since we don't
+ //want changes in the filter editor to propagate into here right away.
+ this.view.filter_rules = filterRules
+ this.reload()
+ this.saveDocumentListView()
+ }
+
+ get filterRules(): FilterRule[] {
+ return this.view.filter_rules
+ }
+
+ set sortField(field: string) {
+ this.view.sort_field = field
+ this.saveDocumentListView()
+ this.reload()
+ }
+
+ get sortField(): string {
+ return this.view.sort_field
+ }
+
+ set sortReverse(reverse: boolean) {
+ this.view.sort_reverse = reverse
+ this.saveDocumentListView()
+ this.reload()
+ }
+
+ get sortReverse(): boolean {
+ return this.view.sort_reverse
+ }
+
+ private saveDocumentListView() {
+ sessionStorage.setItem(DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG, JSON.stringify(this.documentListView))
+ }
+
+ getLastPage(): number {
+ return Math.ceil(this.collectionSize / this.currentPageSize)
+ }
+
+ hasNext(doc: number) {
+ if (this.documents) {
+ let index = this.documents.findIndex(d => d.id == doc)
+ return index != -1 && (this.currentPage < this.getLastPage() || (index + 1) < this.documents.length)
+ }
+ }
+
+ getNext(currentDocId: number): Observable {
+ return new Observable(nextDocId => {
+ if (this.documents != null) {
+
+ let index = this.documents.findIndex(d => d.id == currentDocId)
+
+ if (index != -1 && (index + 1) < this.documents.length) {
+ nextDocId.next(this.documents[index+1].id)
+ nextDocId.complete()
+ } else if (index != -1 && this.currentPage < this.getLastPage()) {
+ this.currentPage += 1
+ this.reload(() => {
+ nextDocId.next(this.documents[0].id)
+ nextDocId.complete()
+ })
+ } else {
+ nextDocId.complete()
+ }
+ } else {
+ nextDocId.complete()
+ }
+ })
+ }
+
+ updatePageSize() {
+ let newPageSize = +localStorage.getItem(GENERAL_SETTINGS.DOCUMENT_LIST_SIZE) || GENERAL_SETTINGS.DOCUMENT_LIST_SIZE_DEFAULT
+ if (newPageSize != this.currentPageSize) {
+ this.currentPageSize = newPageSize
+ }
+ }
+
+ constructor(private documentService: DocumentService) {
+ let documentListViewConfigJson = sessionStorage.getItem(DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG)
+ if (documentListViewConfigJson) {
+ try {
+ this.documentListView = JSON.parse(documentListViewConfigJson)
+ } catch (e) {
+ sessionStorage.removeItem(DOCUMENT_LIST_SERVICE.CURRENT_VIEW_CONFIG)
+ this.documentListView = null
+ }
+ }
+ if (!this.documentListView || !this.documentListView.filter_rules || !this.documentListView.sort_reverse || !this.documentListView.sort_field) {
+ this.documentListView = {
+ filter_rules: [],
+ sort_reverse: true,
+ sort_field: 'created'
+ }
+ }
+ }
+}
diff --git a/src-ui/src/app/services/open-documents.service.spec.ts b/src-ui/src/app/services/open-documents.service.spec.ts
new file mode 100644
index 000000000..c0d0c7a4b
--- /dev/null
+++ b/src-ui/src/app/services/open-documents.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { OpenDocumentsService } from './open-documents.service';
+
+describe('OpenDocumentsService', () => {
+ let service: OpenDocumentsService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(OpenDocumentsService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/services/open-documents.service.ts b/src-ui/src/app/services/open-documents.service.ts
new file mode 100644
index 000000000..e37f5db8c
--- /dev/null
+++ b/src-ui/src/app/services/open-documents.service.ts
@@ -0,0 +1,60 @@
+import { Injectable } from '@angular/core';
+import { PaperlessDocument } from '../data/paperless-document';
+import { OPEN_DOCUMENT_SERVICE } from '../data/storage-keys';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class OpenDocumentsService {
+
+ private MAX_OPEN_DOCUMENTS = 5
+
+ constructor() {
+ if (sessionStorage.getItem(OPEN_DOCUMENT_SERVICE.DOCUMENTS)) {
+ try {
+ this.openDocuments = JSON.parse(sessionStorage.getItem(OPEN_DOCUMENT_SERVICE.DOCUMENTS))
+ } catch (e) {
+ sessionStorage.removeItem(OPEN_DOCUMENT_SERVICE.DOCUMENTS)
+ this.openDocuments = []
+ }
+ }
+ }
+
+ private openDocuments: PaperlessDocument[] = []
+
+ getOpenDocuments(): PaperlessDocument[] {
+ return this.openDocuments
+ }
+
+ getOpenDocument(id: number): PaperlessDocument {
+ return this.openDocuments.find(d => d.id == id)
+ }
+
+ openDocument(doc: PaperlessDocument) {
+ if (this.openDocuments.find(d => d.id == doc.id) == null) {
+ this.openDocuments.unshift(doc)
+ if (this.openDocuments.length > this.MAX_OPEN_DOCUMENTS) {
+ this.openDocuments.pop()
+ }
+ this.save()
+ }
+ }
+
+ closeDocument(doc: PaperlessDocument) {
+ let index = this.openDocuments.findIndex(d => d.id == doc.id)
+ if (index > -1) {
+ this.openDocuments.splice(index, 1)
+ this.save()
+ }
+ }
+
+ closeAll() {
+ this.openDocuments.splice(0, this.openDocuments.length)
+ this.save()
+ }
+
+ save() {
+ sessionStorage.setItem(OPEN_DOCUMENT_SERVICE.DOCUMENTS, JSON.stringify(this.openDocuments))
+ }
+
+}
diff --git a/src-ui/src/app/services/rest/abstract-paperless-service.spec.ts b/src-ui/src/app/services/rest/abstract-paperless-service.spec.ts
new file mode 100644
index 000000000..f95a9cb39
--- /dev/null
+++ b/src-ui/src/app/services/rest/abstract-paperless-service.spec.ts
@@ -0,0 +1,7 @@
+import { AbstractPaperlessService } from './abstract-paperless-service';
+
+describe('AbstractPaperlessService', () => {
+ it('should create an instance', () => {
+ expect(new AbstractPaperlessService()).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/services/rest/abstract-paperless-service.ts b/src-ui/src/app/services/rest/abstract-paperless-service.ts
new file mode 100644
index 000000000..93e1a0c85
--- /dev/null
+++ b/src-ui/src/app/services/rest/abstract-paperless-service.ts
@@ -0,0 +1,101 @@
+import { HttpClient, HttpParams } from '@angular/common/http'
+import { Observable } from 'rxjs'
+import { map, publishReplay, refCount } from 'rxjs/operators'
+import { ObjectWithId } from 'src/app/data/object-with-id'
+import { Results } from 'src/app/data/results'
+import { environment } from 'src/environments/environment'
+
+export abstract class AbstractPaperlessService {
+
+ protected baseUrl: string = environment.apiBaseUrl
+
+ constructor(protected http: HttpClient, private resourceName: string) { }
+
+ protected getResourceUrl(id?: number, action?: string): string {
+ let url = `${this.baseUrl}${this.resourceName}/`
+ if (id) {
+ url += `${id}/`
+ }
+ if (action) {
+ url += `${action}/`
+ }
+ return url
+ }
+
+ private getOrderingQueryParam(sortField: string, sortReverse: boolean) {
+ if (sortField) {
+ return (sortReverse ? '-' : '') + sortField
+ } else {
+ return null
+ }
+ }
+
+ list(page?: number, pageSize?: number, sortField?: string, sortReverse?: boolean, extraParams?): Observable> {
+ let httpParams = new HttpParams()
+ if (page) {
+ httpParams = httpParams.set('page', page.toString())
+ }
+ if (pageSize) {
+ httpParams = httpParams.set('page_size', pageSize.toString())
+ }
+ let ordering = this.getOrderingQueryParam(sortField, sortReverse)
+ if (ordering) {
+ httpParams = httpParams.set('ordering', ordering)
+ }
+ for (let extraParamKey in extraParams) {
+ if (extraParams[extraParamKey] != null) {
+ httpParams = httpParams.set(extraParamKey, extraParams[extraParamKey])
+ }
+ }
+ return this.http.get>(this.getResourceUrl(), {params: httpParams})
+ }
+
+ private _listAll: Observable>
+
+ listAll(ordering?: string, extraParams?): Observable> {
+ if (!this._listAll) {
+ this._listAll = this.list(1, 100000, ordering, extraParams).pipe(
+ publishReplay(1),
+ refCount()
+ )
+ }
+ return this._listAll
+ }
+
+ getCached(id: number): Observable {
+ return this.listAll().pipe(
+ map(list => list.results.find(o => o.id == id))
+ )
+ }
+
+ getCachedMany(ids: number[]): Observable {
+ return this.listAll().pipe(
+ map(list => ids.map(id => list.results.find(o => o.id == id)))
+ )
+ }
+
+ get(id: number): Observable {
+ return this.http.get(this.getResourceUrl(id))
+ }
+
+ create(o: T): Observable {
+ this._listAll = null
+ return this.http.post(this.getResourceUrl(), o)
+ }
+
+ delete(o: T): Observable {
+ this._listAll = null
+ return this.http.delete(this.getResourceUrl(o.id))
+ }
+
+ update(o: T): Observable {
+ this._listAll = null
+ return this.http.put(this.getResourceUrl(o.id), o)
+ }
+
+ patch(o: T): Observable {
+ this._listAll = null
+ return this.http.patch(this.getResourceUrl(o.id), o)
+ }
+
+}
diff --git a/src-ui/src/app/services/rest/correspondent.service.spec.ts b/src-ui/src/app/services/rest/correspondent.service.spec.ts
new file mode 100644
index 000000000..a3377fded
--- /dev/null
+++ b/src-ui/src/app/services/rest/correspondent.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { CorrespondentService } from './correspondent.service';
+
+describe('CorrespondentService', () => {
+ let service: CorrespondentService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(CorrespondentService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/services/rest/correspondent.service.ts b/src-ui/src/app/services/rest/correspondent.service.ts
new file mode 100644
index 000000000..a609b7dd8
--- /dev/null
+++ b/src-ui/src/app/services/rest/correspondent.service.ts
@@ -0,0 +1,15 @@
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { PaperlessCorrespondent } from 'src/app/data/paperless-correspondent';
+import { AbstractPaperlessService } from './abstract-paperless-service';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class CorrespondentService extends AbstractPaperlessService {
+
+ constructor(http: HttpClient) {
+ super(http, 'correspondents')
+ }
+
+}
diff --git a/src-ui/src/app/services/rest/document-type.service.spec.ts b/src-ui/src/app/services/rest/document-type.service.spec.ts
new file mode 100644
index 000000000..adcca11ef
--- /dev/null
+++ b/src-ui/src/app/services/rest/document-type.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { DocumentTypeService } from './document-type.service';
+
+describe('DocumentTypeService', () => {
+ let service: DocumentTypeService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(DocumentTypeService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/services/rest/document-type.service.ts b/src-ui/src/app/services/rest/document-type.service.ts
new file mode 100644
index 000000000..a3ba0d858
--- /dev/null
+++ b/src-ui/src/app/services/rest/document-type.service.ts
@@ -0,0 +1,14 @@
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { PaperlessDocumentType } from 'src/app/data/paperless-document-type';
+import { AbstractPaperlessService } from './abstract-paperless-service';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class DocumentTypeService extends AbstractPaperlessService {
+
+ constructor(http: HttpClient) {
+ super(http, 'document_types')
+ }
+}
diff --git a/src-ui/src/app/services/rest/document.service.spec.ts b/src-ui/src/app/services/rest/document.service.spec.ts
new file mode 100644
index 000000000..49475199d
--- /dev/null
+++ b/src-ui/src/app/services/rest/document.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { DocumentService } from './document.service';
+
+describe('DocumentService', () => {
+ let service: DocumentService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(DocumentService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/services/rest/document.service.ts b/src-ui/src/app/services/rest/document.service.ts
new file mode 100644
index 000000000..f50620d23
--- /dev/null
+++ b/src-ui/src/app/services/rest/document.service.ts
@@ -0,0 +1,101 @@
+import { Injectable } from '@angular/core';
+import { PaperlessDocument } from 'src/app/data/paperless-document';
+import { PaperlessDocumentMetadata } from 'src/app/data/paperless-document-metadata';
+import { AbstractPaperlessService } from './abstract-paperless-service';
+import { HttpClient } from '@angular/common/http';
+import { Observable } from 'rxjs';
+import { Results } from 'src/app/data/results';
+import { FilterRule } from 'src/app/data/filter-rule';
+import { map } from 'rxjs/operators';
+import { CorrespondentService } from './correspondent.service';
+import { DocumentTypeService } from './document-type.service';
+import { TagService } from './tag.service';
+import { FILTER_RULE_TYPES } from 'src/app/data/filter-rule-type';
+
+export const DOCUMENT_SORT_FIELDS = [
+ { field: "correspondent__name", name: "Correspondent" },
+ { field: "document_type__name", name: "Document type" },
+ { field: 'title', name: 'Title' },
+ { field: 'archive_serial_number', name: 'ASN' },
+ { field: 'created', name: 'Created' },
+ { field: 'added', name: 'Added' },
+ { field: 'modified', name: 'Modified' }
+]
+
+@Injectable({
+ providedIn: 'root'
+})
+export class DocumentService extends AbstractPaperlessService {
+
+ constructor(http: HttpClient, private correspondentService: CorrespondentService, private documentTypeService: DocumentTypeService, private tagService: TagService) {
+ super(http, 'documents')
+ }
+
+ private filterRulesToQueryParams(filterRules: FilterRule[]) {
+ if (filterRules) {
+ let params = {}
+ for (let rule of filterRules) {
+ let ruleType = FILTER_RULE_TYPES.find(t => t.id == rule.rule_type)
+ if (ruleType.multi) {
+ params[ruleType.filtervar] = params[ruleType.filtervar] ? params[ruleType.filtervar] + "," + rule.value : rule.value
+ } else {
+ params[ruleType.filtervar] = rule.value
+ }
+ }
+ return params
+ } else {
+ return null
+ }
+ }
+
+ addObservablesToDocument(doc: PaperlessDocument) {
+ if (doc.correspondent) {
+ doc.correspondent$ = this.correspondentService.getCached(doc.correspondent)
+ }
+ if (doc.document_type) {
+ doc.document_type$ = this.documentTypeService.getCached(doc.document_type)
+ }
+ if (doc.tags) {
+ doc.tags$ = this.tagService.getCachedMany(doc.tags)
+ }
+ return doc
+ }
+
+ list(page?: number, pageSize?: number, sortField?: string, sortReverse?: boolean, filterRules?: FilterRule[]): Observable> {
+ return super.list(page, pageSize, sortField, sortReverse, this.filterRulesToQueryParams(filterRules)).pipe(
+ map(results => {
+ results.results.forEach(doc => this.addObservablesToDocument(doc))
+ return results
+ })
+ )
+ }
+
+ getPreviewUrl(id: number, original: boolean = false): string {
+ let url = this.getResourceUrl(id, 'preview')
+ if (original) {
+ url += "?original=true"
+ }
+ return url
+ }
+
+ getThumbUrl(id: number): string {
+ return this.getResourceUrl(id, 'thumb')
+ }
+
+ getDownloadUrl(id: number, original: boolean = false): string {
+ let url = this.getResourceUrl(id, 'download')
+ if (original) {
+ url += "?original=true"
+ }
+ return url
+ }
+
+ uploadDocument(formData) {
+ return this.http.post(this.getResourceUrl(null, 'post_document'), formData, {reportProgress: true, observe: "events"})
+ }
+
+ getMetadata(id: number): Observable {
+ return this.http.get(this.getResourceUrl(id, 'metadata'))
+ }
+
+}
diff --git a/src-ui/src/app/services/rest/log.service.spec.ts b/src-ui/src/app/services/rest/log.service.spec.ts
new file mode 100644
index 000000000..4a99f7727
--- /dev/null
+++ b/src-ui/src/app/services/rest/log.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { LogService } from './log.service';
+
+describe('LogService', () => {
+ let service: LogService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(LogService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/services/rest/log.service.ts b/src-ui/src/app/services/rest/log.service.ts
new file mode 100644
index 000000000..797d9b6b9
--- /dev/null
+++ b/src-ui/src/app/services/rest/log.service.ts
@@ -0,0 +1,14 @@
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { PaperlessLog } from 'src/app/data/paperless-log';
+import { AbstractPaperlessService } from './abstract-paperless-service';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class LogService extends AbstractPaperlessService {
+
+ constructor(http: HttpClient) {
+ super(http, 'logs')
+ }
+}
diff --git a/src-ui/src/app/services/rest/saved-view.service.spec.ts b/src-ui/src/app/services/rest/saved-view.service.spec.ts
new file mode 100644
index 000000000..588cf6347
--- /dev/null
+++ b/src-ui/src/app/services/rest/saved-view.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { SavedViewService } from './saved-view.service';
+
+describe('SavedViewService', () => {
+ let service: SavedViewService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(SavedViewService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/services/rest/saved-view.service.ts b/src-ui/src/app/services/rest/saved-view.service.ts
new file mode 100644
index 000000000..9a81e01e5
--- /dev/null
+++ b/src-ui/src/app/services/rest/saved-view.service.ts
@@ -0,0 +1,59 @@
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { combineLatest, Observable } from 'rxjs';
+import { tap } from 'rxjs/operators';
+import { PaperlessSavedView } from 'src/app/data/paperless-saved-view';
+import { AbstractPaperlessService } from './abstract-paperless-service';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class SavedViewService extends AbstractPaperlessService {
+
+ constructor(http: HttpClient) {
+ super(http, 'saved_views')
+ this.reload()
+ }
+
+ private reload() {
+ this.listAll().subscribe(r => this.savedViews = r.results)
+ }
+
+ private savedViews: PaperlessSavedView[] = []
+
+ get allViews() {
+ return this.savedViews
+ }
+
+ get sidebarViews() {
+ return this.savedViews.filter(v => v.show_in_sidebar)
+ }
+
+ get dashboardViews() {
+ return this.savedViews.filter(v => v.show_on_dashboard)
+ }
+
+ create(o: PaperlessSavedView) {
+ return super.create(o).pipe(
+ tap(() => this.reload())
+ )
+ }
+
+ update(o: PaperlessSavedView) {
+ return super.update(o).pipe(
+ tap(() => this.reload())
+ )
+ }
+
+ patchMany(objects: PaperlessSavedView[]): Observable {
+ return combineLatest(objects.map(o => super.patch(o))).pipe(
+ tap(() => this.reload())
+ )
+ }
+
+ delete(o: PaperlessSavedView) {
+ return super.delete(o).pipe(
+ tap(() => this.reload())
+ )
+ }
+}
diff --git a/src-ui/src/app/services/rest/search.service.spec.ts b/src-ui/src/app/services/rest/search.service.spec.ts
new file mode 100644
index 000000000..23c42c7bb
--- /dev/null
+++ b/src-ui/src/app/services/rest/search.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { SearchService } from './search.service';
+
+describe('SearchService', () => {
+ let service: SearchService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(SearchService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/services/rest/search.service.ts b/src-ui/src/app/services/rest/search.service.ts
new file mode 100644
index 000000000..b19a55769
--- /dev/null
+++ b/src-ui/src/app/services/rest/search.service.ts
@@ -0,0 +1,34 @@
+import { HttpClient, HttpParams } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { PaperlessDocument } from 'src/app/data/paperless-document';
+import { SearchResult } from 'src/app/data/search-result';
+import { environment } from 'src/environments/environment';
+import { DocumentService } from './document.service';
+
+
+@Injectable({
+ providedIn: 'root'
+})
+export class SearchService {
+
+ constructor(private http: HttpClient, private documentService: DocumentService) { }
+
+ search(query: string, page?: number): Observable {
+ let httpParams = new HttpParams().set('query', query)
+ if (page) {
+ httpParams = httpParams.set('page', page.toString())
+ }
+ return this.http.get(`${environment.apiBaseUrl}search/`, {params: httpParams}).pipe(
+ map(result => {
+ result.results.forEach(hit => this.documentService.addObservablesToDocument(hit.document))
+ return result
+ })
+ )
+ }
+
+ autocomplete(term: string): Observable {
+ return this.http.get(`${environment.apiBaseUrl}search/autocomplete/`, {params: new HttpParams().set('term', term)})
+ }
+}
diff --git a/src-ui/src/app/services/rest/tag.service.spec.ts b/src-ui/src/app/services/rest/tag.service.spec.ts
new file mode 100644
index 000000000..f091e933f
--- /dev/null
+++ b/src-ui/src/app/services/rest/tag.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { TagService } from './tag.service';
+
+describe('TagService', () => {
+ let service: TagService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(TagService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/services/rest/tag.service.ts b/src-ui/src/app/services/rest/tag.service.ts
new file mode 100644
index 000000000..b4151dbb9
--- /dev/null
+++ b/src-ui/src/app/services/rest/tag.service.ts
@@ -0,0 +1,14 @@
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { PaperlessTag } from 'src/app/data/paperless-tag';
+import { AbstractPaperlessService } from './abstract-paperless-service';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class TagService extends AbstractPaperlessService {
+
+ constructor(http: HttpClient) {
+ super(http, 'tags')
+ }
+}
diff --git a/src-ui/src/app/services/toast.service.spec.ts b/src-ui/src/app/services/toast.service.spec.ts
new file mode 100644
index 000000000..e0413db84
--- /dev/null
+++ b/src-ui/src/app/services/toast.service.spec.ts
@@ -0,0 +1,16 @@
+import { TestBed } from '@angular/core/testing';
+
+import { ToastService } from './toast.service';
+
+describe('ToastService', () => {
+ let service: ToastService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({});
+ service = TestBed.inject(ToastService);
+ });
+
+ it('should be created', () => {
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/src-ui/src/app/services/toast.service.ts b/src-ui/src/app/services/toast.service.ts
new file mode 100644
index 000000000..a3ce060a9
--- /dev/null
+++ b/src-ui/src/app/services/toast.service.ts
@@ -0,0 +1,59 @@
+import { Injectable } from '@angular/core';
+import { Subject, zip } from 'rxjs';
+
+export class Toast {
+
+ static make(title: string, content: string, classname?: string, delay?: number): Toast {
+ let t = new Toast()
+ t.title = title
+ t.content = content
+ t.classname = classname
+ if (delay) {
+ t.delay = delay
+ }
+ return t
+ }
+
+ static makeError(content: string) {
+ return Toast.make("Error", content, null, 10000)
+ }
+
+ title: string
+
+ classname: string
+
+ content: string
+
+ delay: number = 5000
+
+}
+
+@Injectable({
+ providedIn: 'root'
+})
+export class ToastService {
+
+ constructor() { }
+
+ private toasts: Toast[] = []
+
+ private toastsSubject: Subject = new Subject()
+
+ showToast(toast: Toast) {
+ this.toasts.push(toast)
+ this.toastsSubject.next(this.toasts)
+ }
+
+ closeToast(toast: Toast) {
+ let index = this.toasts.findIndex(t => t == toast)
+ if (index > -1) {
+ this.toasts.splice(index, 1)
+ this.toastsSubject.next(this.toasts)
+ }
+ }
+
+ getToasts() {
+ return this.toastsSubject
+ }
+
+}
diff --git a/src-ui/src/assets/.gitkeep b/src-ui/src/assets/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/src-ui/src/assets/bootstrap-icons.svg b/src-ui/src/assets/bootstrap-icons.svg
new file mode 100644
index 000000000..f7731a14b
--- /dev/null
+++ b/src-ui/src/assets/bootstrap-icons.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src-ui/src/assets/document.png b/src-ui/src/assets/document.png
new file mode 100644
index 000000000..8c24f9d6e
Binary files /dev/null and b/src-ui/src/assets/document.png differ
diff --git a/src-ui/src/assets/logo-dark-notext.svg b/src-ui/src/assets/logo-dark-notext.svg
new file mode 100644
index 000000000..38ca9e700
--- /dev/null
+++ b/src-ui/src/assets/logo-dark-notext.svg
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src-ui/src/assets/logo-dark.svg b/src-ui/src/assets/logo-dark.svg
new file mode 100644
index 000000000..bc8ba2b80
--- /dev/null
+++ b/src-ui/src/assets/logo-dark.svg
@@ -0,0 +1,93 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src-ui/src/assets/logo.svg b/src-ui/src/assets/logo.svg
new file mode 100644
index 000000000..5d28e87e9
--- /dev/null
+++ b/src-ui/src/assets/logo.svg
@@ -0,0 +1,84 @@
+
+
+
+
+
+ image/svg+xml
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src-ui/src/assets/save-filter.png b/src-ui/src/assets/save-filter.png
new file mode 100644
index 000000000..0f011f812
Binary files /dev/null and b/src-ui/src/assets/save-filter.png differ
diff --git a/src-ui/src/environments/environment.prod.ts b/src-ui/src/environments/environment.prod.ts
new file mode 100644
index 000000000..f12c6a7cb
--- /dev/null
+++ b/src-ui/src/environments/environment.prod.ts
@@ -0,0 +1,6 @@
+export const environment = {
+ production: true,
+ apiBaseUrl: "/api/",
+ appTitle: "Paperless-ng",
+ version: "0.9.8"
+};
diff --git a/src-ui/src/environments/environment.ts b/src-ui/src/environments/environment.ts
new file mode 100644
index 000000000..29a8f3af6
--- /dev/null
+++ b/src-ui/src/environments/environment.ts
@@ -0,0 +1,19 @@
+// This file can be replaced during build by using the `fileReplacements` array.
+// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
+// The list of file replacements can be found in `angular.json`.
+
+export const environment = {
+ production: false,
+ apiBaseUrl: "http://localhost:8000/api/",
+ appTitle: "Paperless-ng",
+ version: "DEVELOPMENT"
+};
+
+/*
+ * For easier debugging in development mode, you can import the following file
+ * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
+ *
+ * This import should be commented out in production mode because it will have a negative impact
+ * on performance if an error is thrown.
+ */
+// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
diff --git a/src-ui/src/favicon.ico b/src-ui/src/favicon.ico
new file mode 100644
index 000000000..cb57d8b2b
Binary files /dev/null and b/src-ui/src/favicon.ico differ
diff --git a/src-ui/src/index.html b/src-ui/src/index.html
new file mode 100644
index 000000000..f82399ce6
--- /dev/null
+++ b/src-ui/src/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+ Paperless-ng
+
+
+
+
+
+
+
+
diff --git a/src-ui/src/main.ts b/src-ui/src/main.ts
new file mode 100644
index 000000000..c7b673cf4
--- /dev/null
+++ b/src-ui/src/main.ts
@@ -0,0 +1,12 @@
+import { enableProdMode } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+import { environment } from './environments/environment';
+
+if (environment.production) {
+ enableProdMode();
+}
+
+platformBrowserDynamic().bootstrapModule(AppModule)
+ .catch(err => console.error(err));
diff --git a/src-ui/src/polyfills.ts b/src-ui/src/polyfills.ts
new file mode 100644
index 000000000..01e24d6f6
--- /dev/null
+++ b/src-ui/src/polyfills.ts
@@ -0,0 +1,67 @@
+/***************************************************************************************************
+ * Load `$localize` onto the global scope - used if i18n tags appear in Angular templates.
+ */
+import '@angular/localize/init';
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ * file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
+ * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/guide/browser-support
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/** IE10 and IE11 requires the following for NgClass support on SVG elements */
+// import 'classlist.js'; // Run `npm install --save classlist.js`.
+
+/**
+ * Web Animations `@angular/platform-browser/animations`
+ * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
+ * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
+ */
+// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
+
+/**
+ * By default, zone.js will patch all possible macroTask and DomEvents
+ * user can disable parts of macroTask/DomEvents patch by setting following flags
+ * because those flags need to be set before `zone.js` being loaded, and webpack
+ * will put import in the top of bundle, so user need to create a separate file
+ * in this directory (for example: zone-flags.ts), and put the following flags
+ * into that file, and then add the following code before importing zone.js.
+ * import './zone-flags';
+ *
+ * The flags allowed in zone-flags.ts are listed here.
+ *
+ * The following flags will work for all browsers.
+ *
+ * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
+ * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
+ * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
+ *
+ * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
+ * with the following flag, it will bypass `zone.js` patch for IE/Edge
+ *
+ * (window as any).__Zone_enable_cross_context_check = true;
+ *
+ */
+
+/***************************************************************************************************
+ * Zone JS is required by default for Angular itself.
+ */
+import 'zone.js/dist/zone'; // Included with Angular CLI.
+
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
diff --git a/src-ui/src/styles.scss b/src-ui/src/styles.scss
new file mode 100644
index 000000000..b0b66b7f9
--- /dev/null
+++ b/src-ui/src/styles.scss
@@ -0,0 +1,68 @@
+@import "theme";
+
+@import "node_modules/bootstrap/scss/bootstrap";
+
+
+.toolbaricon {
+ width: 1.2em;
+ height: 1.2em;
+}
+
+.buttonicon {
+ width: 1.2em;
+ height: 1.2em;
+}
+
+.sidebaricon {
+ width: 16px;
+ height: 16px;
+ vertical-align: text-bottom;
+}
+
+body {
+ font-size: .875rem;
+}
+
+.form-control-dark {
+ color: #fff;
+ background-color: rgba(255, 255, 255, .1);
+ border-color: rgba(255, 255, 255, .1);
+}
+
+.form-control-dark::placeholder {
+ color: #fff;
+}
+
+.form-control-dark:focus {
+ border-color: transparent;
+ box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
+}
+
+.asc {
+ background-color: #f8f9fa!important;
+}
+
+.asc:after {
+ content: '';
+ transform: rotate(180deg);
+ background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAmxJREFUeAHtmksrRVEUx72fH8CIGQNJkpGUUmakDEiZSJRIZsRQmCkTJRmZmJgQE0kpX0D5DJKJgff7v+ru2u3O3vvc67TOvsdatdrnnP1Y///v7HvvubdbUiIhBISAEBACQkAICAEhIAQ4CXSh2DnyDfmCPEG2Iv9F9MPlM/LHyAecdyMzHYNwR3fdNK/OH9HXl1UCozD24TCvILxizEDWIEzA0FcM8woCgRrJCoS5PIwrANQSMAJX1LEI9bqpQo4JYNFFKRSvIgsxHDVnqZgIkPnNBM0rIGtYk9YOOsqgbgepRCfdbmFtqhFkVEDVPjJp0+Z6e6hRHhqBKgg6ZDCvYBygVmUoEGoh5JTRvIJwhJo1aUOoh4CLPMyvxxi7EWOMgnCGsXXI1GIXlZUYX7ucU+kbR8NW8lh3O7cue0Pk32MKndfUxQFAwxdirk3fHappAnc0oqDPzDfGTBrCfHP04dM4oTV8cxr0SVzH9FF07xD3ib6xCDE+M+aUcVygtWzzbtGX2rPBrEUYfecfQkaFzYi6HjVnGBdtL7epqAlc1+jRdAap74RrnPc4BCijttY2tRcdN0g17w7HqZrXhdJTYAuS3hd8z+vKgK3V1zWPae0mZDMykadBn1hTQBLnZNwVrJpSe/NwEeDsEwCctEOsJTsgxLvCqUl2ACftEGvJDgjxrnBqkh3ASTvEWrIDQrwrnJpkB3DSDrGW7IAQ7wqnJtkBnLRztejXXVu4+mxz/nQ9jR1w5VB86ejLTFcnnDwhzV+F6T+CHZlx6THSjn76eyyBIOPHyDakhBAQAkJACAgBISAEhIAQYCLwC8JxpAmsEGt6AAAAAElFTkSuQmCC") no-repeat;
+ height: 1rem;
+ width: 1rem;
+ display: block;
+ background-size: 1rem;
+ float: right;
+}
+
+.des {
+ background-color: #f8f9fa!important;
+}
+
+.des:after {
+ content: '';
+ background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAmxJREFUeAHtmksrRVEUx72fH8CIGQNJkpGUUmakDEiZSJRIZsRQmCkTJRmZmJgQE0kpX0D5DJKJgff7v+ru2u3O3vvc67TOvsdatdrnnP1Y///v7HvvubdbUiIhBISAEBACQkAICAEhIAQ4CXSh2DnyDfmCPEG2Iv9F9MPlM/LHyAecdyMzHYNwR3fdNK/OH9HXl1UCozD24TCvILxizEDWIEzA0FcM8woCgRrJCoS5PIwrANQSMAJX1LEI9bqpQo4JYNFFKRSvIgsxHDVnqZgIkPnNBM0rIGtYk9YOOsqgbgepRCfdbmFtqhFkVEDVPjJp0+Z6e6hRHhqBKgg6ZDCvYBygVmUoEGoh5JTRvIJwhJo1aUOoh4CLPMyvxxi7EWOMgnCGsXXI1GIXlZUYX7ucU+kbR8NW8lh3O7cue0Pk32MKndfUxQFAwxdirk3fHappAnc0oqDPzDfGTBrCfHP04dM4oTV8cxr0SVzH9FF07xD3ib6xCDE+M+aUcVygtWzzbtGX2rPBrEUYfecfQkaFzYi6HjVnGBdtL7epqAlc1+jRdAap74RrnPc4BCijttY2tRcdN0g17w7HqZrXhdJTYAuS3hd8z+vKgK3V1zWPae0mZDMykadBn1hTQBLnZNwVrJpSe/NwEeDsEwCctEOsJTsgxLvCqUl2ACftEGvJDgjxrnBqkh3ASTvEWrIDQrwrnJpkB3DSDrGW7IAQ7wqnJtkBnLRztejXXVu4+mxz/nQ9jR1w5VB86ejLTFcnnDwhzV+F6T+CHZlx6THSjn76eyyBIOPHyDakhBAQAkJACAgBISAEhIAQYCLwC8JxpAmsEGt6AAAAAElFTkSuQmCC") no-repeat;
+ height: 1rem;
+ width: 1rem;
+ display: block;
+ background-size: 1rem;
+ float: right;
+}
\ No newline at end of file
diff --git a/src-ui/src/test.ts b/src-ui/src/test.ts
new file mode 100644
index 000000000..50193eb0f
--- /dev/null
+++ b/src-ui/src/test.ts
@@ -0,0 +1,25 @@
+// This file is required by karma.conf.js and loads recursively all the .spec and framework files
+
+import 'zone.js/dist/zone-testing';
+import { getTestBed } from '@angular/core/testing';
+import {
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting
+} from '@angular/platform-browser-dynamic/testing';
+
+declare const require: {
+ context(path: string, deep?: boolean, filter?: RegExp): {
+ keys(): string[];
+ (id: string): T;
+ };
+};
+
+// First, initialize the Angular testing environment.
+getTestBed().initTestEnvironment(
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting()
+);
+// Then we find all the tests.
+const context = require.context('./', true, /\.spec\.ts$/);
+// And load the modules.
+context.keys().map(context);
diff --git a/src-ui/src/theme.scss b/src-ui/src/theme.scss
new file mode 100644
index 000000000..88f3ae30f
--- /dev/null
+++ b/src-ui/src/theme.scss
@@ -0,0 +1,6 @@
+$paperless-green: #17541f;
+$primary: #17541f;
+
+$theme-colors: (
+ "primary": $primary
+);
\ No newline at end of file
diff --git a/src-ui/tsconfig.app.json b/src-ui/tsconfig.app.json
new file mode 100644
index 000000000..82d91dc4a
--- /dev/null
+++ b/src-ui/tsconfig.app.json
@@ -0,0 +1,15 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "./out-tsc/app",
+ "types": []
+ },
+ "files": [
+ "src/main.ts",
+ "src/polyfills.ts"
+ ],
+ "include": [
+ "src/**/*.d.ts"
+ ]
+}
diff --git a/src-ui/tsconfig.json b/src-ui/tsconfig.json
new file mode 100644
index 000000000..f69f65417
--- /dev/null
+++ b/src-ui/tsconfig.json
@@ -0,0 +1,20 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "compileOnSave": false,
+ "compilerOptions": {
+ "baseUrl": "./",
+ "outDir": "./dist/out-tsc",
+ "sourceMap": true,
+ "declaration": false,
+ "downlevelIteration": true,
+ "experimentalDecorators": true,
+ "moduleResolution": "node",
+ "importHelpers": true,
+ "target": "es2015",
+ "module": "es2020",
+ "lib": [
+ "es2018",
+ "dom"
+ ]
+ }
+}
diff --git a/src-ui/tsconfig.spec.json b/src-ui/tsconfig.spec.json
new file mode 100644
index 000000000..092345b02
--- /dev/null
+++ b/src-ui/tsconfig.spec.json
@@ -0,0 +1,18 @@
+/* To learn more about this file see: https://angular.io/config/tsconfig. */
+{
+ "extends": "./tsconfig.json",
+ "compilerOptions": {
+ "outDir": "./out-tsc/spec",
+ "types": [
+ "jasmine"
+ ]
+ },
+ "files": [
+ "src/test.ts",
+ "src/polyfills.ts"
+ ],
+ "include": [
+ "src/**/*.spec.ts",
+ "src/**/*.d.ts"
+ ]
+}
diff --git a/src-ui/tslint.json b/src-ui/tslint.json
new file mode 100644
index 000000000..277c8eba0
--- /dev/null
+++ b/src-ui/tslint.json
@@ -0,0 +1,152 @@
+{
+ "extends": "tslint:recommended",
+ "rulesDirectory": [
+ "codelyzer"
+ ],
+ "rules": {
+ "align": {
+ "options": [
+ "parameters",
+ "statements"
+ ]
+ },
+ "array-type": false,
+ "arrow-return-shorthand": true,
+ "curly": true,
+ "deprecation": {
+ "severity": "warning"
+ },
+ "eofline": true,
+ "import-blacklist": [
+ true,
+ "rxjs/Rx"
+ ],
+ "import-spacing": true,
+ "indent": {
+ "options": [
+ "spaces"
+ ]
+ },
+ "max-classes-per-file": false,
+ "max-line-length": [
+ true,
+ 140
+ ],
+ "member-ordering": [
+ true,
+ {
+ "order": [
+ "static-field",
+ "instance-field",
+ "static-method",
+ "instance-method"
+ ]
+ }
+ ],
+ "no-console": [
+ true,
+ "debug",
+ "info",
+ "time",
+ "timeEnd",
+ "trace"
+ ],
+ "no-empty": false,
+ "no-inferrable-types": [
+ true,
+ "ignore-params"
+ ],
+ "no-non-null-assertion": true,
+ "no-redundant-jsdoc": true,
+ "no-switch-case-fall-through": true,
+ "no-var-requires": false,
+ "object-literal-key-quotes": [
+ true,
+ "as-needed"
+ ],
+ "quotemark": [
+ true,
+ "single"
+ ],
+ "semicolon": {
+ "options": [
+ "always"
+ ]
+ },
+ "space-before-function-paren": {
+ "options": {
+ "anonymous": "never",
+ "asyncArrow": "always",
+ "constructor": "never",
+ "method": "never",
+ "named": "never"
+ }
+ },
+ "typedef": [
+ true,
+ "call-signature"
+ ],
+ "typedef-whitespace": {
+ "options": [
+ {
+ "call-signature": "nospace",
+ "index-signature": "nospace",
+ "parameter": "nospace",
+ "property-declaration": "nospace",
+ "variable-declaration": "nospace"
+ },
+ {
+ "call-signature": "onespace",
+ "index-signature": "onespace",
+ "parameter": "onespace",
+ "property-declaration": "onespace",
+ "variable-declaration": "onespace"
+ }
+ ]
+ },
+ "variable-name": {
+ "options": [
+ "ban-keywords",
+ "check-format",
+ "allow-pascal-case"
+ ]
+ },
+ "whitespace": {
+ "options": [
+ "check-branch",
+ "check-decl",
+ "check-operator",
+ "check-separator",
+ "check-type",
+ "check-typecast"
+ ]
+ },
+ "component-class-suffix": true,
+ "contextual-lifecycle": true,
+ "directive-class-suffix": true,
+ "no-conflicting-lifecycle": true,
+ "no-host-metadata-property": true,
+ "no-input-rename": true,
+ "no-inputs-metadata-property": true,
+ "no-output-native": true,
+ "no-output-on-prefix": true,
+ "no-output-rename": true,
+ "no-outputs-metadata-property": true,
+ "template-banana-in-box": true,
+ "template-no-negated-async": true,
+ "use-lifecycle-interface": true,
+ "use-pipe-transform-interface": true,
+ "directive-selector": [
+ true,
+ "attribute",
+ "app",
+ "camelCase"
+ ],
+ "component-selector": [
+ true,
+ "element",
+ "app",
+ "kebab-case"
+ ]
+ }
+}
diff --git a/src/documents/__init__.py b/src/documents/__init__.py
index 864b5f5fe..5c9f358c3 100644
--- a/src/documents/__init__.py
+++ b/src/documents/__init__.py
@@ -1 +1,2 @@
-from .checks import changed_password_check
+# this is here so that django finds the checks.
+from .checks import *
diff --git a/src/documents/actions.py b/src/documents/actions.py
deleted file mode 100644
index cd2698a2c..000000000
--- a/src/documents/actions.py
+++ /dev/null
@@ -1,146 +0,0 @@
-from django.contrib import messages
-from django.contrib.admin import helpers
-from django.contrib.admin.utils import model_ngettext
-from django.core.exceptions import PermissionDenied
-from django.template.response import TemplateResponse
-
-from documents.models import Correspondent, Tag
-
-
-def select_action(
- modeladmin, request, queryset, title, action, modelclass,
- success_message="", document_action=None, queryset_action=None):
-
- opts = modeladmin.model._meta
- app_label = opts.app_label
-
- if not modeladmin.has_change_permission(request):
- raise PermissionDenied
-
- if request.POST.get('post'):
- n = queryset.count()
- selected_object = modelclass.objects.get(id=request.POST.get('obj_id'))
- if n:
- for document in queryset:
- if document_action:
- document_action(document, selected_object)
- document_display = str(document)
- modeladmin.log_change(request, document, document_display)
- if queryset_action:
- queryset_action(queryset, selected_object)
-
- modeladmin.message_user(request, success_message % {
- "selected_object": selected_object.name,
- "count": n,
- "items": model_ngettext(modeladmin.opts, n)
- }, messages.SUCCESS)
-
- # Return None to display the change list page again.
- return None
-
- context = dict(
- modeladmin.admin_site.each_context(request),
- title=title,
- queryset=queryset,
- opts=opts,
- action_checkbox_name=helpers.ACTION_CHECKBOX_NAME,
- media=modeladmin.media,
- action=action,
- objects=modelclass.objects.all(),
- itemname=model_ngettext(modelclass, 1)
- )
-
- request.current_app = modeladmin.admin_site.name
-
- return TemplateResponse(
- request,
- "admin/{}/{}/select_object.html".format(app_label, opts.model_name),
- context
- )
-
-
-def simple_action(
- modeladmin, request, queryset, success_message="",
- document_action=None, queryset_action=None):
-
- if not modeladmin.has_change_permission(request):
- raise PermissionDenied
-
- n = queryset.count()
- if n:
- for document in queryset:
- if document_action:
- document_action(document)
- document_display = str(document)
- modeladmin.log_change(request, document, document_display)
- if queryset_action:
- queryset_action(queryset)
- modeladmin.message_user(request, success_message % {
- "count": n, "items": model_ngettext(modeladmin.opts, n)
- }, messages.SUCCESS)
-
- # Return None to display the change list page again.
- return None
-
-
-def add_tag_to_selected(modeladmin, request, queryset):
- return select_action(
- modeladmin=modeladmin,
- request=request,
- queryset=queryset,
- title="Add tag to multiple documents",
- action="add_tag_to_selected",
- modelclass=Tag,
- success_message="Successfully added tag %(selected_object)s to "
- "%(count)d %(items)s.",
- document_action=lambda doc, tag: doc.tags.add(tag)
- )
-
-
-def remove_tag_from_selected(modeladmin, request, queryset):
- return select_action(
- modeladmin=modeladmin,
- request=request,
- queryset=queryset,
- title="Remove tag from multiple documents",
- action="remove_tag_from_selected",
- modelclass=Tag,
- success_message="Successfully removed tag %(selected_object)s from "
- "%(count)d %(items)s.",
- document_action=lambda doc, tag: doc.tags.remove(tag)
- )
-
-
-def set_correspondent_on_selected(modeladmin, request, queryset):
-
- return select_action(
- modeladmin=modeladmin,
- request=request,
- queryset=queryset,
- title="Set correspondent on multiple documents",
- action="set_correspondent_on_selected",
- modelclass=Correspondent,
- success_message="Successfully set correspondent %(selected_object)s "
- "on %(count)d %(items)s.",
- queryset_action=lambda qs, corr: qs.update(correspondent=corr)
- )
-
-
-def remove_correspondent_from_selected(modeladmin, request, queryset):
- return simple_action(
- modeladmin=modeladmin,
- request=request,
- queryset=queryset,
- success_message="Successfully removed correspondent from %(count)d "
- "%(items)s.",
- queryset_action=lambda qs: qs.update(correspondent=None)
- )
-
-
-add_tag_to_selected.short_description = "Add tag to selected documents"
-remove_tag_from_selected.short_description = \
- "Remove tag from selected documents"
-set_correspondent_on_selected.short_description = \
- "Set correspondent on selected documents"
-remove_correspondent_from_selected.short_description = \
- "Remove correspondent from selected documents"
diff --git a/src/documents/admin.py b/src/documents/admin.py
old mode 100644
new mode 100755
index a7c9e828e..78437f91c
--- a/src/documents/admin.py
+++ b/src/documents/admin.py
@@ -1,205 +1,78 @@
-from datetime import datetime, timedelta
-
-from django.conf import settings
-from django.contrib import admin, messages
-from django.contrib.admin.templatetags.admin_urls import add_preserved_filters
-from django.contrib.auth.models import Group, User
-from django.db import models
-from django.http import HttpResponseRedirect
-from django.templatetags.static import static
-from django.urls import reverse
+from django.contrib import admin
from django.utils.html import format_html, format_html_join
-from django.utils.http import urlquote
from django.utils.safestring import mark_safe
-from djangoql.admin import DjangoQLSearchMixin
+from whoosh.writing import AsyncWriter
-from documents.actions import (
- add_tag_to_selected,
- remove_correspondent_from_selected,
- remove_tag_from_selected,
- set_correspondent_on_selected
-)
-
-from .models import Correspondent, Document, Log, Tag
+from . import index
+from .models import Correspondent, Document, DocumentType, Log, Tag, \
+ SavedView, SavedViewFilterRule
-class FinancialYearFilter(admin.SimpleListFilter):
-
- title = "Financial Year"
- parameter_name = "fy"
- _fy_wraps = None
-
- def _fy_start(self, year):
- """Return date of the start of financial year for the given year."""
- fy_start = "{}-{}".format(str(year), settings.FY_START)
- return datetime.strptime(fy_start, "%Y-%m-%d").date()
-
- def _fy_end(self, year):
- """Return date of the end of financial year for the given year."""
- fy_end = "{}-{}".format(str(year), settings.FY_END)
- return datetime.strptime(fy_end, "%Y-%m-%d").date()
-
- def _fy_does_wrap(self):
- """Return whether the financial year spans across two years."""
- if self._fy_wraps is None:
- start = "{}".format(settings.FY_START)
- start = datetime.strptime(start, "%m-%d").date()
- end = "{}".format(settings.FY_END)
- end = datetime.strptime(end, "%m-%d").date()
- self._fy_wraps = end < start
-
- return self._fy_wraps
-
- def _determine_fy(self, date):
- """Return a (query, display) financial year tuple of the given date."""
- if self._fy_does_wrap():
- fy_start = self._fy_start(date.year)
-
- if date.date() >= fy_start:
- query = "{}-{}".format(date.year, date.year + 1)
- else:
- query = "{}-{}".format(date.year - 1, date.year)
-
- # To keep it simple we use the same string for both
- # query parameter and the display.
- return query, query
-
- else:
- query = "{0}-{0}".format(date.year)
- display = "{}".format(date.year)
- return query, display
-
- def lookups(self, request, model_admin):
- if not settings.FY_START or not settings.FY_END:
- return None
-
- r = []
- for document in Document.objects.all():
- r.append(self._determine_fy(document.created))
-
- return sorted(set(r), key=lambda x: x[0], reverse=True)
-
- def queryset(self, request, queryset):
- if not self.value() or not settings.FY_START or not settings.FY_END:
- return None
-
- start, end = self.value().split("-")
- return queryset.filter(created__gte=self._fy_start(start),
- created__lte=self._fy_end(end))
-
-
-class RecentCorrespondentFilter(admin.RelatedFieldListFilter):
- """
- If PAPERLESS_RECENT_CORRESPONDENT_YEARS is set, we limit the available
- correspondents to documents sent our way over the past ``n`` years.
- """
-
- def field_choices(self, field, request, model_admin):
-
- years = settings.PAPERLESS_RECENT_CORRESPONDENT_YEARS
- correspondents = Correspondent.objects.all()
-
- if years and years > 0:
- self.title = "Correspondent (Recent)"
- days = 365 * years
- correspondents = correspondents.filter(
- documents__created__gte=datetime.now() - timedelta(days=days)
- ).distinct()
-
- return [(c.id, c.name) for c in correspondents]
-
-
-class CommonAdmin(admin.ModelAdmin):
- list_per_page = settings.PAPERLESS_LIST_PER_PAGE
-
-
-class CorrespondentAdmin(CommonAdmin):
+class CorrespondentAdmin(admin.ModelAdmin):
list_display = (
"name",
"match",
- "matching_algorithm",
- "document_count",
- "last_correspondence"
+ "matching_algorithm"
)
list_filter = ("matching_algorithm",)
list_editable = ("match", "matching_algorithm")
- readonly_fields = ("slug",)
- def get_queryset(self, request):
- qs = super(CorrespondentAdmin, self).get_queryset(request)
- qs = qs.annotate(
- document_count=models.Count("documents"),
- last_correspondence=models.Max("documents__created")
- )
- return qs
-
- def document_count(self, obj):
- return obj.document_count
- document_count.admin_order_field = "document_count"
-
- def last_correspondence(self, obj):
- return obj.last_correspondence
- last_correspondence.admin_order_field = "last_correspondence"
-
-
-class TagAdmin(CommonAdmin):
+class TagAdmin(admin.ModelAdmin):
list_display = (
- "name", "colour", "match", "matching_algorithm", "document_count")
+ "name",
+ "colour",
+ "match",
+ "matching_algorithm"
+ )
list_filter = ("colour", "matching_algorithm")
list_editable = ("colour", "match", "matching_algorithm")
- readonly_fields = ("slug",)
- class Media:
- js = ("js/colours.js",)
+class DocumentTypeAdmin(admin.ModelAdmin):
- def get_queryset(self, request):
- qs = super(TagAdmin, self).get_queryset(request)
- qs = qs.annotate(document_count=models.Count("documents"))
- return qs
-
- def document_count(self, obj):
- return obj.document_count
- document_count.admin_order_field = "document_count"
+ list_display = (
+ "name",
+ "match",
+ "matching_algorithm"
+ )
+ list_filter = ("matching_algorithm",)
+ list_editable = ("match", "matching_algorithm")
-class DocumentAdmin(DjangoQLSearchMixin, CommonAdmin):
-
- class Media:
- css = {
- "all": ("paperless.css",)
- }
+class DocumentAdmin(admin.ModelAdmin):
search_fields = ("correspondent__name", "title", "content", "tags__name")
- readonly_fields = ("added", "file_type", "storage_type",)
- list_display = ("title", "created", "added", "thumbnail", "correspondent",
- "tags_")
+ readonly_fields = (
+ "added",
+ "modified",
+ "mime_type",
+ "storage_type",
+ "filename")
+
+ list_display_links = ("title",)
+
+ list_display = (
+ "correspondent",
+ "title",
+ "tags_",
+ "created",
+ )
+
list_filter = (
+ "document_type",
"tags",
- ("correspondent", RecentCorrespondentFilter),
- FinancialYearFilter
+ "correspondent"
)
filter_horizontal = ("tags",)
- ordering = ["-created", "correspondent"]
-
- actions = [
- add_tag_to_selected,
- remove_tag_from_selected,
- set_correspondent_on_selected,
- remove_correspondent_from_selected
- ]
+ ordering = ["-created"]
date_hierarchy = "created"
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.document_queue = []
-
def has_add_permission(self, request):
return False
@@ -207,127 +80,31 @@ class DocumentAdmin(DjangoQLSearchMixin, CommonAdmin):
return obj.created.date().strftime("%Y-%m-%d")
created_.short_description = "Created"
- def changelist_view(self, request, extra_context=None):
+ def delete_queryset(self, request, queryset):
+ ix = index.open_index()
+ with AsyncWriter(ix) as writer:
+ for o in queryset:
+ index.remove_document(writer, o)
+ super(DocumentAdmin, self).delete_queryset(request, queryset)
- response = super().changelist_view(
- request,
- extra_context=extra_context
- )
+ def delete_model(self, request, obj):
+ index.remove_document_from_index(obj)
+ super(DocumentAdmin, self).delete_model(request, obj)
- if request.method == "GET":
- cl = self.get_changelist_instance(request)
- self.document_queue = [doc.id for doc in cl.queryset]
-
- return response
-
- def change_view(self, request, object_id=None, form_url='',
- extra_context=None):
-
- extra_context = extra_context or {}
-
- if self.document_queue and object_id:
- if int(object_id) in self.document_queue:
- # There is a queue of documents
- current_index = self.document_queue.index(int(object_id))
- if current_index < len(self.document_queue) - 1:
- # ... and there are still documents in the queue
- extra_context["next_object"] = self.document_queue[
- current_index + 1
- ]
-
- return super(DocumentAdmin, self).change_view(
- request,
- object_id,
- form_url,
- extra_context=extra_context,
- )
-
- def response_change(self, request, obj):
-
- # This is mostly copied from ModelAdmin.response_change()
- opts = self.model._meta
- preserved_filters = self.get_preserved_filters(request)
-
- msg_dict = {
- "name": opts.verbose_name,
- "obj": format_html(
- '{} ',
- urlquote(request.path),
- obj
- ),
- }
- if "_saveandeditnext" in request.POST:
- msg = format_html(
- 'The {name} "{obj}" was changed successfully. '
- 'Editing next object.',
- **msg_dict
- )
- self.message_user(request, msg, messages.SUCCESS)
- redirect_url = reverse(
- "admin:{}_{}_change".format(opts.app_label, opts.model_name),
- args=(request.POST["_next_object"],),
- current_app=self.admin_site.name
- )
- redirect_url = add_preserved_filters(
- {
- "preserved_filters": preserved_filters,
- "opts": opts
- },
- redirect_url
- )
- return HttpResponseRedirect(redirect_url)
-
- return super().response_change(request, obj)
-
- @mark_safe
- def thumbnail(self, obj):
- return self._html_tag(
- "a",
- self._html_tag(
- "img",
- src=reverse("fetch", kwargs={"kind": "thumb", "pk": obj.pk}),
- width=180,
- alt="Thumbnail of {}".format(obj.file_name),
- title=obj.file_name
- ),
- href=obj.download_url
- )
+ def save_model(self, request, obj, form, change):
+ index.add_or_update_document(obj)
+ super(DocumentAdmin, self).save_model(request, obj, form, change)
@mark_safe
def tags_(self, obj):
r = ""
for tag in obj.tags.all():
- colour = tag.get_colour_display()
r += self._html_tag(
- "a",
- tag.slug,
- **{
- "class": "tag",
- "style": "background-color: {};".format(colour),
- "href": "{}?tags__id__exact={}".format(
- reverse("admin:documents_document_changelist"),
- tag.pk
- )
- }
+ "span",
+ tag.name + ", "
)
return r
- @mark_safe
- def document(self, obj):
- # TODO: is this method even used anymore?
- return self._html_tag(
- "a",
- self._html_tag(
- "img",
- src=static("documents/img/{}.png".format(obj.file_type)),
- width=22,
- height=22,
- alt=obj.file_type,
- title=obj.file_name
- ),
- href=obj.download_url
- )
-
@staticmethod
def _html_tag(kind, inside=None, **kwargs):
attributes = format_html_join(' ', '{}="{}"', kwargs.items())
@@ -339,18 +116,38 @@ class DocumentAdmin(DjangoQLSearchMixin, CommonAdmin):
return format_html("<{} {}/>", kind, attributes)
-class LogAdmin(CommonAdmin):
+class LogAdmin(admin.ModelAdmin):
+
+ def has_add_permission(self, request):
+ return False
+
+ def has_change_permission(self, request, obj=None):
+ return False
list_display = ("created", "message", "level",)
list_filter = ("level", "created",)
+ ordering = ('-created',)
+
+ list_display_links = ("created", "message")
+
+
+class RuleInline(admin.TabularInline):
+ model = SavedViewFilterRule
+
+
+class SavedViewAdmin(admin.ModelAdmin):
+
+ list_display = ("name", "user")
+
+ inlines = [
+ RuleInline
+ ]
+
admin.site.register(Correspondent, CorrespondentAdmin)
admin.site.register(Tag, TagAdmin)
+admin.site.register(DocumentType, DocumentTypeAdmin)
admin.site.register(Document, DocumentAdmin)
admin.site.register(Log, LogAdmin)
-
-
-# Unless we implement multi-user, these default registrations don't make sense.
-admin.site.unregister(Group)
-admin.site.unregister(User)
+admin.site.register(SavedView, SavedViewAdmin)
diff --git a/src/documents/apps.py b/src/documents/apps.py
index 435e1afa5..2cd7d6c0e 100644
--- a/src/documents/apps.py
+++ b/src/documents/apps.py
@@ -1,5 +1,4 @@
from django.apps import AppConfig
-from django.db.models.signals import post_delete
class DocumentsConfig(AppConfig):
@@ -11,21 +10,25 @@ class DocumentsConfig(AppConfig):
from .signals import document_consumption_started
from .signals import document_consumption_finished
from .signals.handlers import (
- set_correspondent,
- set_tags,
+ add_inbox_tags,
run_pre_consume_script,
run_post_consume_script,
- cleanup_document_deletion,
- set_log_entry
+ set_log_entry,
+ set_correspondent,
+ set_document_type,
+ set_tags,
+ add_to_index
+
)
document_consumption_started.connect(run_pre_consume_script)
- document_consumption_finished.connect(set_tags)
+ document_consumption_finished.connect(add_inbox_tags)
document_consumption_finished.connect(set_correspondent)
+ document_consumption_finished.connect(set_document_type)
+ document_consumption_finished.connect(set_tags)
document_consumption_finished.connect(set_log_entry)
+ document_consumption_finished.connect(add_to_index)
document_consumption_finished.connect(run_post_consume_script)
- post_delete.connect(cleanup_document_deletion)
-
AppConfig.ready(self)
diff --git a/src/documents/checks.py b/src/documents/checks.py
index 3310b1806..b6da5bfc9 100644
--- a/src/documents/checks.py
+++ b/src/documents/checks.py
@@ -2,8 +2,11 @@ import textwrap
from django.conf import settings
from django.core.checks import Error, register
+from django.core.exceptions import FieldError
from django.db.utils import OperationalError, ProgrammingError
+from documents.signals import document_consumer_declaration
+
@register()
def changed_password_check(app_configs, **kwargs):
@@ -14,7 +17,7 @@ def changed_password_check(app_configs, **kwargs):
try:
encrypted_doc = Document.objects.filter(
storage_type=Document.STORAGE_TYPE_GPG).first()
- except (OperationalError, ProgrammingError):
+ except (OperationalError, ProgrammingError, FieldError):
return [] # No documents table yet
if encrypted_doc:
@@ -37,3 +40,17 @@ def changed_password_check(app_configs, **kwargs):
"""))]
return []
+
+
+@register()
+def parser_check(app_configs, **kwargs):
+
+ parsers = []
+ for response in document_consumer_declaration.send(None):
+ parsers.append(response[1])
+
+ if len(parsers) == 0:
+ return [Error("No parsers found. This is a bug. The consumer won't be "
+ "able to onsume any documents without parsers.")]
+ else:
+ return []
diff --git a/src/documents/classifier.py b/src/documents/classifier.py
new file mode 100755
index 000000000..60c9abeec
--- /dev/null
+++ b/src/documents/classifier.py
@@ -0,0 +1,256 @@
+import hashlib
+import logging
+import os
+import pickle
+import re
+
+from django.conf import settings
+from sklearn.feature_extraction.text import CountVectorizer
+from sklearn.neural_network import MLPClassifier
+from sklearn.preprocessing import MultiLabelBinarizer, LabelBinarizer
+from sklearn.utils.multiclass import type_of_target
+
+from documents.models import Document, MatchingModel
+
+
+class IncompatibleClassifierVersionError(Exception):
+ pass
+
+
+logger = logging.getLogger(__name__)
+
+
+def preprocess_content(content):
+ content = content.lower().strip()
+ content = re.sub(r"\s+", " ", content)
+ return content
+
+
+class DocumentClassifier(object):
+
+ FORMAT_VERSION = 6
+
+ def __init__(self):
+ # mtime of the model file on disk. used to prevent reloading when
+ # nothing has changed.
+ self.classifier_version = 0
+
+ # hash of the training data. used to prevent re-training when the
+ # training data has not changed.
+ self.data_hash = None
+
+ self.data_vectorizer = None
+ self.tags_binarizer = None
+ self.tags_classifier = None
+ self.correspondent_classifier = None
+ self.document_type_classifier = None
+
+ def reload(self):
+ if os.path.getmtime(settings.MODEL_FILE) > self.classifier_version:
+ with open(settings.MODEL_FILE, "rb") as f:
+ schema_version = pickle.load(f)
+
+ if schema_version != self.FORMAT_VERSION:
+ raise IncompatibleClassifierVersionError(
+ "Cannor load classifier, incompatible versions.")
+ else:
+ if self.classifier_version > 0:
+ # Don't be confused by this check. It's simply here
+ # so that we wont log anything on initial reload.
+ logger.info("Classifier updated on disk, "
+ "reloading classifier models")
+ self.data_hash = pickle.load(f)
+ self.data_vectorizer = pickle.load(f)
+ self.tags_binarizer = pickle.load(f)
+
+ self.tags_classifier = pickle.load(f)
+ self.correspondent_classifier = pickle.load(f)
+ self.document_type_classifier = pickle.load(f)
+ self.classifier_version = os.path.getmtime(settings.MODEL_FILE)
+
+ def save_classifier(self):
+ with open(settings.MODEL_FILE, "wb") as f:
+ pickle.dump(self.FORMAT_VERSION, f)
+ pickle.dump(self.data_hash, f)
+ pickle.dump(self.data_vectorizer, f)
+
+ pickle.dump(self.tags_binarizer, f)
+
+ pickle.dump(self.tags_classifier, f)
+ pickle.dump(self.correspondent_classifier, f)
+ pickle.dump(self.document_type_classifier, f)
+
+ def train(self):
+ data = list()
+ labels_tags = list()
+ labels_correspondent = list()
+ labels_document_type = list()
+
+ # Step 1: Extract and preprocess training data from the database.
+ logging.getLogger(__name__).debug("Gathering data from database...")
+ m = hashlib.sha1()
+ for doc in Document.objects.order_by('pk').exclude(tags__is_inbox_tag=True): # NOQA: E501
+ preprocessed_content = preprocess_content(doc.content)
+ m.update(preprocessed_content.encode('utf-8'))
+ data.append(preprocessed_content)
+
+ y = -1
+ dt = doc.document_type
+ if dt and dt.matching_algorithm == MatchingModel.MATCH_AUTO:
+ y = dt.pk
+ m.update(y.to_bytes(4, 'little', signed=True))
+ labels_document_type.append(y)
+
+ y = -1
+ cor = doc.correspondent
+ if cor and cor.matching_algorithm == MatchingModel.MATCH_AUTO:
+ y = cor.pk
+ m.update(y.to_bytes(4, 'little', signed=True))
+ labels_correspondent.append(y)
+
+ tags = [tag.pk for tag in doc.tags.filter(
+ matching_algorithm=MatchingModel.MATCH_AUTO
+ )]
+ m.update(bytearray(tags))
+ labels_tags.append(tags)
+
+ if not data:
+ raise ValueError("No training data available.")
+
+ new_data_hash = m.digest()
+
+ if self.data_hash and new_data_hash == self.data_hash:
+ return False
+
+ labels_tags_unique = set([tag for tags in labels_tags for tag in tags])
+
+ num_tags = len(labels_tags_unique)
+
+ # substract 1 since -1 (null) is also part of the classes.
+
+ # union with {-1} accounts for cases where all documents have
+ # correspondents and types assigned, so -1 isnt part of labels_x, which
+ # it usually is.
+ num_correspondents = len(set(labels_correspondent) | {-1}) - 1
+ num_document_types = len(set(labels_document_type) | {-1}) - 1
+
+ logging.getLogger(__name__).debug(
+ "{} documents, {} tag(s), {} correspondent(s), "
+ "{} document type(s).".format(
+ len(data),
+ num_tags,
+ num_correspondents,
+ num_document_types
+ )
+ )
+
+ # Step 2: vectorize data
+ logging.getLogger(__name__).debug("Vectorizing data...")
+ self.data_vectorizer = CountVectorizer(
+ analyzer="word",
+ ngram_range=(1, 2),
+ min_df=0.01
+ )
+ data_vectorized = self.data_vectorizer.fit_transform(data)
+
+ # Step 3: train the classifiers
+ if num_tags > 0:
+ logging.getLogger(__name__).debug("Training tags classifier...")
+
+ if num_tags == 1:
+ # Special case where only one tag has auto:
+ # Fallback to binary classification.
+ labels_tags = [label[0] if len(label) == 1 else -1
+ for label in labels_tags]
+ self.tags_binarizer = LabelBinarizer()
+ labels_tags_vectorized = self.tags_binarizer.fit_transform(
+ labels_tags).ravel()
+ else:
+ self.tags_binarizer = MultiLabelBinarizer()
+ labels_tags_vectorized = self.tags_binarizer.fit_transform(
+ labels_tags)
+
+ self.tags_classifier = MLPClassifier(tol=0.01)
+ self.tags_classifier.fit(data_vectorized, labels_tags_vectorized)
+ else:
+ self.tags_classifier = None
+ logging.getLogger(__name__).debug(
+ "There are no tags. Not training tags classifier."
+ )
+
+ if num_correspondents > 0:
+ logging.getLogger(__name__).debug(
+ "Training correspondent classifier..."
+ )
+ self.correspondent_classifier = MLPClassifier(tol=0.01)
+ self.correspondent_classifier.fit(
+ data_vectorized,
+ labels_correspondent
+ )
+ else:
+ self.correspondent_classifier = None
+ logging.getLogger(__name__).debug(
+ "There are no correspondents. Not training correspondent "
+ "classifier."
+ )
+
+ if num_document_types > 0:
+ logging.getLogger(__name__).debug(
+ "Training document type classifier..."
+ )
+ self.document_type_classifier = MLPClassifier(tol=0.01)
+ self.document_type_classifier.fit(
+ data_vectorized,
+ labels_document_type
+ )
+ else:
+ self.document_type_classifier = None
+ logging.getLogger(__name__).debug(
+ "There are no document types. Not training document type "
+ "classifier."
+ )
+
+ self.data_hash = new_data_hash
+
+ return True
+
+ def predict_correspondent(self, content):
+ if self.correspondent_classifier:
+ X = self.data_vectorizer.transform([preprocess_content(content)])
+ correspondent_id = self.correspondent_classifier.predict(X)
+ if correspondent_id != -1:
+ return correspondent_id
+ else:
+ return None
+ else:
+ return None
+
+ def predict_document_type(self, content):
+ if self.document_type_classifier:
+ X = self.data_vectorizer.transform([preprocess_content(content)])
+ document_type_id = self.document_type_classifier.predict(X)
+ if document_type_id != -1:
+ return document_type_id
+ else:
+ return None
+ else:
+ return None
+
+ def predict_tags(self, content):
+ if self.tags_classifier:
+ X = self.data_vectorizer.transform([preprocess_content(content)])
+ y = self.tags_classifier.predict(X)
+ tags_ids = self.tags_binarizer.inverse_transform(y)[0]
+ if type_of_target(y).startswith('multilabel'):
+ # the usual case when there are multiple tags.
+ return list(tags_ids)
+ elif type_of_target(y) == 'binary' and tags_ids != -1:
+ # This is for when we have binary classification with only one
+ # tag and the result is to assign this tag.
+ return [tags_ids]
+ else:
+ # Usually binary as well with -1 as the result, but we're
+ # going to catch everything else here as well.
+ return []
+ else:
+ return []
diff --git a/src/documents/consumer.py b/src/documents/consumer.py
old mode 100644
new mode 100755
index 282b688b1..e4da51f1d
--- a/src/documents/consumer.py
+++ b/src/documents/consumer.py
@@ -1,21 +1,22 @@
-from django.db import transaction
import datetime
import hashlib
import logging
import os
-import re
-import time
-import uuid
-from operator import itemgetter
+import magic
from django.conf import settings
+from django.db import transaction
+from django.db.models import Q
from django.utils import timezone
-from paperless.db import GnuPG
+from filelock import FileLock
-from .models import Document, FileInfo, Tag
-from .parsers import ParseError
+from .classifier import DocumentClassifier, IncompatibleClassifierVersionError
+from .file_handling import create_source_path_directory, \
+ generate_unique_filename
+from .loggers import LoggingMixin
+from .models import Document, FileInfo, Correspondent, DocumentType, Tag
+from .parsers import ParseError, get_parser_class_for_mime_type, parse_date
from .signals import (
- document_consumer_declaration,
document_consumption_finished,
document_consumption_started
)
@@ -25,242 +26,263 @@ class ConsumerError(Exception):
pass
-class Consumer:
- """
- Loop over every file found in CONSUMPTION_DIR and:
- 1. Convert it to a greyscale pnm
- 2. Use tesseract on the pnm
- 3. Store the document in the MEDIA_ROOT with optional encryption
- 4. Store the OCR'd text in the database
- 5. Delete the document and image(s)
- """
+class Consumer(LoggingMixin):
- # Files are considered ready for consumption if they have been unmodified
- # for this duration
- FILES_MIN_UNMODIFIED_DURATION = 0.5
+ def __init__(self):
+ super().__init__()
+ self.path = None
+ self.filename = None
+ self.override_title = None
+ self.override_correspondent_id = None
+ self.override_tag_ids = None
+ self.override_document_type_id = None
- def __init__(self, consume=settings.CONSUMPTION_DIR,
- scratch=settings.SCRATCH_DIR):
-
- self.logger = logging.getLogger(__name__)
- self.logging_group = None
-
- self._ignore = []
- self.consume = consume
- self.scratch = scratch
-
- os.makedirs(self.scratch, exist_ok=True)
-
- self.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
- if settings.PASSPHRASE:
- self.storage_type = Document.STORAGE_TYPE_GPG
-
- if not self.consume:
- raise ConsumerError(
- "The CONSUMPTION_DIR settings variable does not appear to be "
- "set."
- )
-
- if not os.path.exists(self.consume):
- raise ConsumerError(
- "Consumption directory {} does not exist".format(self.consume))
-
- self.parsers = []
- for response in document_consumer_declaration.send(self):
- self.parsers.append(response[1])
-
- if not self.parsers:
- raise ConsumerError(
- "No parsers could be found, not even the default. "
- "This is a problem."
- )
-
- def log(self, level, message):
- getattr(self.logger, level)(message, extra={
- "group": self.logging_group
- })
-
- def consume_new_files(self):
- """
- Find non-ignored files in consumption dir and consume them if they have
- been unmodified for FILES_MIN_UNMODIFIED_DURATION.
- """
- ignored_files = []
- files = []
- for entry in os.scandir(self.consume):
- if entry.is_file():
- file = (entry.path, entry.stat().st_mtime)
- if file in self._ignore:
- ignored_files.append(file)
- else:
- files.append(file)
- else:
- self.logger.warning(
- "Skipping %s as it is not a file",
- entry.path
- )
-
- if not files:
- return
-
- # Set _ignore to only include files that still exist.
- # This keeps it from growing indefinitely.
- self._ignore[:] = ignored_files
-
- files_old_to_new = sorted(files, key=itemgetter(1))
-
- time.sleep(self.FILES_MIN_UNMODIFIED_DURATION)
-
- for file, mtime in files_old_to_new:
- if mtime == os.path.getmtime(file):
- # File has not been modified and can be consumed
- if not self.try_consume_file(file):
- self._ignore.append((file, mtime))
-
- @transaction.atomic
- def try_consume_file(self, file):
- """
- Return True if file was consumed
- """
-
- if not re.match(FileInfo.REGEXES["title"], file):
- return False
-
- doc = file
-
- if self._is_duplicate(doc):
+ def pre_check_file_exists(self):
+ if not os.path.isfile(self.path):
self.log(
- "info",
- "Skipping {} as it appears to be a duplicate".format(doc)
+ "error",
+ "Cannot consume {}: It is not a file.".format(self.path)
)
- return False
+ raise ConsumerError("Cannot consume {}: It is not a file".format(
+ self.path))
- parser_class = self._get_parser_class(doc)
+ def pre_check_duplicate(self):
+ with open(self.path, "rb") as f:
+ checksum = hashlib.md5(f.read()).hexdigest()
+ if Document.objects.filter(Q(checksum=checksum) | Q(archive_checksum=checksum)).exists(): # NOQA: E501
+ if settings.CONSUMER_DELETE_DUPLICATES:
+ os.unlink(self.path)
+ self.log(
+ "error",
+ "Not consuming {}: It is a duplicate.".format(self.filename)
+ )
+ raise ConsumerError(
+ "Not consuming {}: It is a duplicate.".format(self.filename)
+ )
+
+ def pre_check_directories(self):
+ os.makedirs(settings.SCRATCH_DIR, exist_ok=True)
+ os.makedirs(settings.THUMBNAIL_DIR, exist_ok=True)
+ os.makedirs(settings.ORIGINALS_DIR, exist_ok=True)
+ os.makedirs(settings.ARCHIVE_DIR, exist_ok=True)
+
+ def try_consume_file(self,
+ path,
+ override_filename=None,
+ override_title=None,
+ override_correspondent_id=None,
+ override_document_type_id=None,
+ override_tag_ids=None):
+ """
+ Return the document object if it was successfully created.
+ """
+
+ self.path = path
+ self.filename = override_filename or os.path.basename(path)
+ self.override_title = override_title
+ self.override_correspondent_id = override_correspondent_id
+ self.override_document_type_id = override_document_type_id
+ self.override_tag_ids = override_tag_ids
+
+ # this is for grouping logging entries for this particular file
+ # together.
+
+ self.renew_logging_group()
+
+ # Make sure that preconditions for consuming the file are met.
+
+ self.pre_check_file_exists()
+ self.pre_check_directories()
+ self.pre_check_duplicate()
+
+ self.log("info", "Consuming {}".format(self.filename))
+
+ # Determine the parser class.
+
+ mime_type = magic.from_file(self.path, mime=True)
+
+ parser_class = get_parser_class_for_mime_type(mime_type)
if not parser_class:
- self.log(
- "error", "No parsers could be found for {}".format(doc))
- return False
+ raise ConsumerError(f"No parsers abvailable for {self.filename}")
+ else:
+ self.log("debug",
+ f"Parser: {parser_class.__name__} "
+ f"based on mime type {mime_type}")
- self.logging_group = uuid.uuid4()
-
- self.log("info", "Consuming {}".format(doc))
+ # Notify all listeners that we're going to do some work.
document_consumption_started.send(
sender=self.__class__,
- filename=doc,
+ filename=self.path,
logging_group=self.logging_group
)
- parsed_document = parser_class(doc)
+ # This doesn't parse the document yet, but gives us a parser.
+
+ document_parser = parser_class(self.logging_group)
+
+ # However, this already created working directories which we have to
+ # clean up.
+
+ # Parse the document. This may take some time.
try:
- thumbnail = parsed_document.get_optimised_thumbnail()
- date = parsed_document.get_date()
- document = self._store(
- parsed_document.get_text(),
- doc,
- thumbnail,
- date
- )
+ self.log("debug", "Parsing {}...".format(self.filename))
+ document_parser.parse(self.path, mime_type)
+
+ self.log("debug", f"Generating thumbnail for {self.filename}...")
+ thumbnail = document_parser.get_optimised_thumbnail(
+ self.path, mime_type)
+
+ text = document_parser.get_text()
+ date = document_parser.get_date()
+ if not date:
+ date = parse_date(self.filename, text)
+ archive_path = document_parser.get_archive_path()
+
except ParseError as e:
- self.log("error", "PARSE FAILURE for {}: {}".format(doc, e))
- parsed_document.cleanup()
- return False
- else:
- parsed_document.cleanup()
- self._cleanup_doc(doc)
-
+ document_parser.cleanup()
self.log(
- "info",
- "Document {} consumption finished".format(document)
+ "error",
+ f"Error while consuming document {self.filename}: {e}")
+ raise ConsumerError(e)
+
+ # Prepare the document classifier.
+
+ # TODO: I don't really like to do this here, but this way we avoid
+ # reloading the classifier multiple times, since there are multiple
+ # post-consume hooks that all require the classifier.
+
+ try:
+ classifier = DocumentClassifier()
+ classifier.reload()
+ except (FileNotFoundError, IncompatibleClassifierVersionError) as e:
+ self.log(
+ "warning",
+ f"Cannot classify documents: {e}.")
+ classifier = None
+
+ # now that everything is done, we can start to store the document
+ # in the system. This will be a transaction and reasonably fast.
+ try:
+ with transaction.atomic():
+
+ # store the document.
+ document = self._store(
+ text=text,
+ date=date,
+ mime_type=mime_type
+ )
+
+ # If we get here, it was successful. Proceed with post-consume
+ # hooks. If they fail, nothing will get changed.
+
+ document_consumption_finished.send(
+ sender=self.__class__,
+ document=document,
+ logging_group=self.logging_group,
+ classifier=classifier
+ )
+
+ # After everything is in the database, copy the files into
+ # place. If this fails, we'll also rollback the transaction.
+ with FileLock(settings.MEDIA_LOCK):
+ document.filename = generate_unique_filename(
+ document, settings.ORIGINALS_DIR)
+ create_source_path_directory(document.source_path)
+
+ self._write(document.storage_type,
+ self.path, document.source_path)
+
+ self._write(document.storage_type,
+ thumbnail, document.thumbnail_path)
+
+ if archive_path and os.path.isfile(archive_path):
+ create_source_path_directory(document.archive_path)
+ self._write(document.storage_type,
+ archive_path, document.archive_path)
+
+ with open(archive_path, 'rb') as f:
+ document.archive_checksum = hashlib.md5(
+ f.read()).hexdigest()
+
+ # Don't save with the lock active. Saving will cause the file
+ # renaming logic to aquire the lock as well.
+ document.save()
+
+ # Delete the file only if it was successfully consumed
+ self.log("debug", "Deleting file {}".format(self.path))
+ os.unlink(self.path)
+ except Exception as e:
+ self.log(
+ "error",
+ f"The following error occured while consuming "
+ f"{self.filename}: {e}"
)
-
- document_consumption_finished.send(
- sender=self.__class__,
- document=document,
- logging_group=self.logging_group
- )
- return True
-
- def _get_parser_class(self, doc):
- """
- Determine the appropriate parser class based on the file
- """
-
- options = []
- for parser in self.parsers:
- result = parser(doc)
- if result:
- options.append(result)
+ raise ConsumerError(e)
+ finally:
+ document_parser.cleanup()
self.log(
"info",
- "Parsers available: {}".format(
- ", ".join([str(o["parser"].__name__) for o in options])
- )
+ "Document {} consumption finished".format(document)
)
- if not options:
- return None
+ return document
- # Return the parser with the highest weight.
- return sorted(
- options, key=lambda _: _["weight"], reverse=True)[0]["parser"]
+ def _store(self, text, date, mime_type):
- def _store(self, text, doc, thumbnail, date):
+ # If someone gave us the original filename, use it instead of doc.
- file_info = FileInfo.from_path(doc)
+ file_info = FileInfo.from_filename(self.filename)
- stats = os.stat(doc)
+ stats = os.stat(self.path)
self.log("debug", "Saving record to database")
created = file_info.created or date or timezone.make_aware(
- datetime.datetime.fromtimestamp(stats.st_mtime))
+ datetime.datetime.fromtimestamp(stats.st_mtime))
- with open(doc, "rb") as f:
+ storage_type = Document.STORAGE_TYPE_UNENCRYPTED
+
+ with open(self.path, "rb") as f:
document = Document.objects.create(
correspondent=file_info.correspondent,
- title=file_info.title,
+ title=(self.override_title or file_info.title)[:127],
content=text,
- file_type=file_info.extension,
+ mime_type=mime_type,
checksum=hashlib.md5(f.read()).hexdigest(),
created=created,
modified=created,
- storage_type=self.storage_type
+ storage_type=storage_type
)
- relevant_tags = set(list(Tag.match_all(text)) + list(file_info.tags))
+ relevant_tags = set(file_info.tags)
if relevant_tags:
- tag_names = ", ".join([t.slug for t in relevant_tags])
+ tag_names = ", ".join([t.name for t in relevant_tags])
self.log("debug", "Tagging with {}".format(tag_names))
document.tags.add(*relevant_tags)
- self._write(document, doc, document.source_path)
- self._write(document, thumbnail, document.thumbnail_path)
+ self.apply_overrides(document)
- document.set_filename(document.source_filename)
document.save()
- self.log("info", "Completed")
-
return document
- def _write(self, document, source, target):
+ def apply_overrides(self, document):
+ if self.override_correspondent_id:
+ document.correspondent = Correspondent.objects.get(
+ pk=self.override_correspondent_id)
+
+ if self.override_document_type_id:
+ document.document_type = DocumentType.objects.get(
+ pk=self.override_document_type_id)
+
+ if self.override_tag_ids:
+ for tag_id in self.override_tag_ids:
+ document.tags.add(Tag.objects.get(pk=tag_id))
+
+ def _write(self, storage_type, source, target):
with open(source, "rb") as read_file:
with open(target, "wb") as write_file:
- if document.storage_type == Document.STORAGE_TYPE_UNENCRYPTED:
- write_file.write(read_file.read())
- return
- self.log("debug", "Encrypting")
- write_file.write(GnuPG.encrypted(read_file))
-
- def _cleanup_doc(self, doc):
- self.log("debug", "Deleting document {}".format(doc))
- os.unlink(doc)
-
- @staticmethod
- def _is_duplicate(doc):
- with open(doc, "rb") as f:
- checksum = hashlib.md5(f.read()).hexdigest()
- return Document.objects.filter(checksum=checksum).exists()
+ write_file.write(read_file.read())
diff --git a/src/documents/file_handling.py b/src/documents/file_handling.py
new file mode 100644
index 000000000..5643756ac
--- /dev/null
+++ b/src/documents/file_handling.py
@@ -0,0 +1,160 @@
+import datetime
+import logging
+import os
+from collections import defaultdict
+
+import pathvalidate
+from django.conf import settings
+from django.template.defaultfilters import slugify
+
+
+class defaultdictNoStr(defaultdict):
+
+ def __str__(self):
+ raise ValueError("Don't use {tags} directly.")
+
+
+def create_source_path_directory(source_path):
+ os.makedirs(os.path.dirname(source_path), exist_ok=True)
+
+
+def delete_empty_directories(directory, root):
+ if not os.path.isdir(directory):
+ return
+
+ # Go up in the directory hierarchy and try to delete all directories
+ directory = os.path.normpath(directory)
+ root = os.path.normpath(root)
+
+ if not directory.startswith(root + os.path.sep):
+ # don't do anything outside our originals folder.
+
+ # append os.path.set so that we avoid these cases:
+ # directory = /home/originals2/test
+ # root = /home/originals ("/" gets appended and startswith fails)
+ return
+
+ while directory != root:
+ if not os.listdir(directory):
+ # it's empty
+ try:
+ os.rmdir(directory)
+ except OSError:
+ # whatever. empty directories aren't that bad anyway.
+ return
+ else:
+ # it's not empty.
+ return
+
+ # go one level up
+ directory = os.path.normpath(os.path.dirname(directory))
+
+
+def many_to_dictionary(field):
+ # Converts ManyToManyField to dictionary by assuming, that field
+ # entries contain an _ or - which will be used as a delimiter
+ mydictionary = dict()
+
+ for index, t in enumerate(field.all()):
+ # Populate tag names by index
+ mydictionary[index] = slugify(t.name)
+
+ # Find delimiter
+ delimiter = t.name.find('_')
+
+ if delimiter == -1:
+ delimiter = t.name.find('-')
+
+ if delimiter == -1:
+ continue
+
+ key = t.name[:delimiter]
+ value = t.name[delimiter + 1:]
+
+ mydictionary[slugify(key)] = slugify(value)
+
+ return mydictionary
+
+
+def generate_unique_filename(doc, root):
+ counter = 0
+
+ while True:
+ new_filename = generate_filename(doc, counter)
+ if new_filename == doc.filename:
+ # still the same as before.
+ return new_filename
+
+ if os.path.exists(os.path.join(root, new_filename)):
+ counter += 1
+ else:
+ return new_filename
+
+
+def generate_filename(doc, counter=0):
+ path = ""
+
+ try:
+ if settings.PAPERLESS_FILENAME_FORMAT is not None:
+ tags = defaultdictNoStr(lambda: slugify(None),
+ many_to_dictionary(doc.tags))
+
+ tag_list = pathvalidate.sanitize_filename(
+ ",".join([tag.name for tag in doc.tags.all()]),
+ replacement_text="-"
+ )
+
+ if doc.correspondent:
+ correspondent = pathvalidate.sanitize_filename(
+ doc.correspondent.name, replacement_text="-"
+ )
+ else:
+ correspondent = "none"
+
+ if doc.document_type:
+ document_type = pathvalidate.sanitize_filename(
+ doc.document_type.name, replacement_text="-"
+ )
+ else:
+ document_type = "none"
+
+ path = settings.PAPERLESS_FILENAME_FORMAT.format(
+ title=pathvalidate.sanitize_filename(
+ doc.title, replacement_text="-"),
+ correspondent=correspondent,
+ document_type=document_type,
+ created=datetime.date.isoformat(doc.created),
+ created_year=doc.created.year if doc.created else "none",
+ created_month=f"{doc.created.month:02}" if doc.created else "none", # NOQA: E501
+ created_day=f"{doc.created.day:02}" if doc.created else "none",
+ added=datetime.date.isoformat(doc.added),
+ added_year=doc.added.year if doc.added else "none",
+ added_month=f"{doc.added.month:02}" if doc.added else "none",
+ added_day=f"{doc.added.day:02}" if doc.added else "none",
+ tags=tags,
+ tag_list=tag_list
+ ).strip()
+
+ path = path.strip(os.sep)
+
+ except (ValueError, KeyError, IndexError):
+ logging.getLogger(__name__).warning(
+ f"Invalid PAPERLESS_FILENAME_FORMAT: "
+ f"{settings.PAPERLESS_FILENAME_FORMAT}, falling back to default")
+
+ counter_str = f"_{counter:02}" if counter else ""
+ if len(path) > 0:
+ filename = f"{path}{counter_str}{doc.file_type}"
+ else:
+ filename = f"{doc.pk:07}{counter_str}{doc.file_type}"
+
+ # Append .gpg for encrypted files
+ if doc.storage_type == doc.STORAGE_TYPE_GPG:
+ filename += ".gpg"
+
+ return filename
+
+
+def archive_name_from_filename(filename):
+
+ return os.path.splitext(filename)[0] + ".pdf"
diff --git a/src/documents/filters.py b/src/documents/filters.py
old mode 100644
new mode 100755
index 2b7c9cc9f..b3c92eba3
--- a/src/documents/filters.py
+++ b/src/documents/filters.py
@@ -1,12 +1,11 @@
-from django_filters.rest_framework import BooleanFilter, FilterSet
+from django_filters.rest_framework import BooleanFilter, FilterSet, Filter
-from .models import Correspondent, Document, Tag
+from .models import Correspondent, Document, Tag, DocumentType, Log
-
-CHAR_KWARGS = (
- "startswith", "endswith", "contains",
- "istartswith", "iendswith", "icontains"
-)
+CHAR_KWARGS = ["istartswith", "iendswith", "icontains", "iexact"]
+ID_KWARGS = ["in", "exact"]
+INT_KWARGS = ["exact", "gt", "gte", "lt", "lte"]
+DATE_KWARGS = ["year", "month", "day", "date__gt", "gt", "date__lt", "lt"]
class CorrespondentFilterSet(FilterSet):
@@ -14,11 +13,7 @@ class CorrespondentFilterSet(FilterSet):
class Meta:
model = Correspondent
fields = {
- "name": [
- "startswith", "endswith", "contains",
- "istartswith", "iendswith", "icontains"
- ],
- "slug": ["istartswith", "iendswith", "icontains"]
+ "name": CHAR_KWARGS
}
@@ -27,34 +22,102 @@ class TagFilterSet(FilterSet):
class Meta:
model = Tag
fields = {
- "name": [
- "startswith", "endswith", "contains",
- "istartswith", "iendswith", "icontains"
- ],
- "slug": ["istartswith", "iendswith", "icontains"]
+ "name": CHAR_KWARGS
}
+class DocumentTypeFilterSet(FilterSet):
+
+ class Meta:
+ model = DocumentType
+ fields = {
+ "name": CHAR_KWARGS
+ }
+
+
+class TagsFilter(Filter):
+
+ def __init__(self, exclude=False):
+ super(TagsFilter, self).__init__()
+ self.exclude = exclude
+
+ def filter(self, qs, value):
+ if not value:
+ return qs
+
+ try:
+ tag_ids = [int(x) for x in value.split(',')]
+ except ValueError:
+ return qs
+
+ for tag_id in tag_ids:
+ if self.exclude:
+ qs = qs.exclude(tags__id=tag_id)
+ else:
+ qs = qs.filter(tags__id=tag_id)
+
+ return qs
+
+
+class InboxFilter(Filter):
+
+ def filter(self, qs, value):
+ if value == 'true':
+ return qs.filter(tags__is_inbox_tag=True)
+ elif value == 'false':
+ return qs.exclude(tags__is_inbox_tag=True)
+ else:
+ return qs
+
+
class DocumentFilterSet(FilterSet):
- tags_empty = BooleanFilter(
+ is_tagged = BooleanFilter(
label="Is tagged",
field_name="tags",
lookup_expr="isnull",
exclude=True
)
+ tags__id__all = TagsFilter()
+
+ tags__id__none = TagsFilter(exclude=True)
+
+ is_in_inbox = InboxFilter()
+
class Meta:
model = Document
fields = {
"title": CHAR_KWARGS,
- "content": ("contains", "icontains"),
+ "content": CHAR_KWARGS,
+ "archive_serial_number": INT_KWARGS,
+
+ "created": DATE_KWARGS,
+ "added": DATE_KWARGS,
+ "modified": DATE_KWARGS,
+
+ "correspondent__id": ID_KWARGS,
"correspondent__name": CHAR_KWARGS,
- "correspondent__slug": CHAR_KWARGS,
+ "tags__id": ID_KWARGS,
"tags__name": CHAR_KWARGS,
- "tags__slug": CHAR_KWARGS,
+
+ "document_type__id": ID_KWARGS,
+ "document_type__name": CHAR_KWARGS,
+
+ }
+
+
+class LogFilterSet(FilterSet):
+
+ class Meta:
+ model = Log
+ fields = {
+
+ "level": INT_KWARGS,
+ "created": DATE_KWARGS,
+ "group": ID_KWARGS
}
diff --git a/src/documents/forms.py b/src/documents/forms.py
deleted file mode 100644
index 3cdef448e..000000000
--- a/src/documents/forms.py
+++ /dev/null
@@ -1,100 +0,0 @@
-import magic
-import os
-
-from datetime import datetime
-from time import mktime
-
-from django import forms
-from django.conf import settings
-
-from .models import Document, Correspondent
-
-
-class UploadForm(forms.Form):
-
- TYPE_LOOKUP = {
- "application/pdf": Document.TYPE_PDF,
- "image/png": Document.TYPE_PNG,
- "image/jpeg": Document.TYPE_JPG,
- "image/gif": Document.TYPE_GIF,
- "image/tiff": Document.TYPE_TIF,
- }
-
- correspondent = forms.CharField(
- max_length=Correspondent._meta.get_field("name").max_length,
- required=False
- )
- title = forms.CharField(
- max_length=Document._meta.get_field("title").max_length,
- required=False
- )
- document = forms.FileField()
-
- def __init__(self, *args, **kwargs):
- forms.Form.__init__(self, *args, **kwargs)
- self._file_type = None
-
- def clean_correspondent(self):
- """
- I suppose it might look cleaner to use .get_or_create() here, but that
- would also allow someone to fill up the db with bogus correspondents
- before all validation was met.
- """
-
- corresp = self.cleaned_data.get("correspondent")
-
- if not corresp:
- return None
-
- if not Correspondent.SAFE_REGEX.match(corresp) or " - " in corresp:
- raise forms.ValidationError(
- "That correspondent name is suspicious.")
-
- return corresp
-
- def clean_title(self):
-
- title = self.cleaned_data.get("title")
-
- if not title:
- return None
-
- if not Correspondent.SAFE_REGEX.match(title) or " - " in title:
- raise forms.ValidationError("That title is suspicious.")
-
- return title
-
- def clean_document(self):
-
- document = self.cleaned_data.get("document").read()
-
- with magic.Magic(flags=magic.MAGIC_MIME_TYPE) as m:
- file_type = m.id_buffer(document)
-
- if file_type not in self.TYPE_LOOKUP:
- raise forms.ValidationError("The file type is invalid.")
-
- self._file_type = self.TYPE_LOOKUP[file_type]
-
- return document
-
- def save(self):
- """
- Since the consumer already does a lot of work, it's easier just to save
- to-be-consumed files to the consumption directory rather than have the
- form do that as well. Think of it as a poor-man's queue server.
- """
-
- correspondent = self.cleaned_data.get("correspondent")
- title = self.cleaned_data.get("title")
- document = self.cleaned_data.get("document")
-
- t = int(mktime(datetime.now().timetuple()))
- file_name = os.path.join(
- settings.CONSUMPTION_DIR,
- "{} - {}.{}".format(correspondent, title, self._file_type)
- )
-
- with open(file_name, "wb") as f:
- f.write(document)
- os.utime(file_name, times=(t, t))
diff --git a/src/documents/index.py b/src/documents/index.py
new file mode 100644
index 000000000..53bf34542
--- /dev/null
+++ b/src/documents/index.py
@@ -0,0 +1,154 @@
+import logging
+import os
+from contextlib import contextmanager
+
+from django.conf import settings
+from whoosh import highlight
+from whoosh.fields import Schema, TEXT, NUMERIC, KEYWORD, DATETIME
+from whoosh.highlight import Formatter, get_text
+from whoosh.index import create_in, exists_in, open_dir
+from whoosh.qparser import MultifieldParser
+from whoosh.qparser.dateparse import DateParserPlugin
+from whoosh.writing import AsyncWriter
+
+
+logger = logging.getLogger(__name__)
+
+
+class JsonFormatter(Formatter):
+ def __init__(self):
+ self.seen = {}
+
+ def format_token(self, text, token, replace=False):
+ seen = self.seen
+ ttext = self._text(get_text(text, token, replace))
+ if ttext in seen:
+ termnum = seen[ttext]
+ else:
+ termnum = len(seen)
+ seen[ttext] = termnum
+
+ return {'text': ttext, 'term': termnum}
+
+ def format_fragment(self, fragment, replace=False):
+ output = []
+ index = fragment.startchar
+ text = fragment.text
+
+ for t in fragment.matches:
+ if t.startchar is None:
+ continue
+ if t.startchar < index:
+ continue
+ if t.startchar > index:
+ output.append({'text': text[index:t.startchar]})
+ output.append(self.format_token(text, t, replace))
+ index = t.endchar
+ if index < fragment.endchar:
+ output.append({'text': text[index:fragment.endchar]})
+ return output
+
+ def format(self, fragments, replace=False):
+ output = []
+ for fragment in fragments:
+ output.append(self.format_fragment(fragment, replace=replace))
+ return output
+
+
+def get_schema():
+ return Schema(
+ id=NUMERIC(stored=True, unique=True, numtype=int),
+ title=TEXT(stored=True),
+ content=TEXT(),
+ correspondent=TEXT(stored=True),
+ tag=KEYWORD(stored=True, commas=True, scorable=True, lowercase=True),
+ type=TEXT(stored=True),
+ created=DATETIME(stored=True, sortable=True),
+ modified=DATETIME(stored=True, sortable=True),
+ added=DATETIME(stored=True, sortable=True),
+ )
+
+
+def open_index(recreate=False):
+ try:
+ if exists_in(settings.INDEX_DIR) and not recreate:
+ return open_dir(settings.INDEX_DIR, schema=get_schema())
+ except Exception as e:
+ logger.error(f"Error while opening the index: {e}, recreating.")
+
+ if not os.path.isdir(settings.INDEX_DIR):
+ os.makedirs(settings.INDEX_DIR, exist_ok=True)
+ return create_in(settings.INDEX_DIR, get_schema())
+
+
+def update_document(writer, doc):
+ # TODO: this line caused many issues all around, since:
+ # We need to make sure that this method does not get called with
+ # deserialized documents (i.e, document objects that don't come from
+ # Django's ORM interfaces directly.
+ logger.debug("Indexing {}...".format(doc))
+ tags = ",".join([t.name for t in doc.tags.all()])
+ writer.update_document(
+ id=doc.pk,
+ title=doc.title,
+ content=doc.content,
+ correspondent=doc.correspondent.name if doc.correspondent else None,
+ tag=tags if tags else None,
+ type=doc.document_type.name if doc.document_type else None,
+ created=doc.created,
+ added=doc.added,
+ modified=doc.modified,
+ )
+
+
+def remove_document(writer, doc):
+ # TODO: see above.
+ logger.debug("Removing {} from index...".format(doc))
+ writer.delete_by_term('id', doc.pk)
+
+
+def add_or_update_document(document):
+ ix = open_index()
+ with AsyncWriter(ix) as writer:
+ update_document(writer, document)
+
+
+def remove_document_from_index(document):
+ ix = open_index()
+ with AsyncWriter(ix) as writer:
+ remove_document(writer, document)
+
+
+@contextmanager
+def query_page(ix, querystring, page):
+ searcher = ix.searcher()
+ try:
+ qp = MultifieldParser(
+ ["content", "title", "correspondent", "tag", "type"],
+ ix.schema)
+ qp.add_plugin(DateParserPlugin())
+
+ q = qp.parse(querystring)
+ result_page = searcher.search_page(q, page)
+ result_page.results.fragmenter = highlight.ContextFragmenter(
+ surround=50)
+ result_page.results.formatter = JsonFormatter()
+
+ corrected = searcher.correct_query(q, querystring)
+ if corrected.query != q:
+ corrected_query = corrected.string
+ else:
+ corrected_query = None
+
+ yield result_page, corrected_query
+ finally:
+ searcher.close()
+
+
+def autocomplete(ix, term, limit=10):
+ with ix.reader() as reader:
+ terms = []
+ for (score, t) in reader.most_distinctive_terms(
+ "content", number=limit, prefix=term.lower()):
+ terms.append(t)
+ return terms
diff --git a/src/documents/loggers.py b/src/documents/loggers.py
index a35841299..863bc0c34 100644
--- a/src/documents/loggers.py
+++ b/src/documents/loggers.py
@@ -1,15 +1,13 @@
import logging
+import uuid
+
+from django.conf import settings
-class PaperlessLogger(logging.StreamHandler):
- """
- A logger smart enough to know to log some kinds of messages to the database
- for later retrieval in a pretty interface.
- """
-
+class PaperlessHandler(logging.Handler):
def emit(self, record):
-
- logging.StreamHandler.emit(self, record)
+ if settings.DISABLE_DBHANDLER:
+ return
# We have to do the import here or Django will barf when it tries to
# load this because the apps aren't loaded at that point
@@ -21,3 +19,19 @@ class PaperlessLogger(logging.StreamHandler):
kwargs["group"] = record.group
Log.objects.create(**kwargs)
+
+
+class LoggingMixin:
+
+ logging_group = None
+
+ def renew_logging_group(self):
+ self.logging_group = uuid.uuid4()
+
+ def log(self, level, message, **kwargs):
+ target = ".".join([self.__class__.__module__, self.__class__.__name__])
+ logger = logging.getLogger(target)
+
+ getattr(logger, level)(message, extra={
+ "group": self.logging_group
+ }, **kwargs)
diff --git a/src/documents/mail.py b/src/documents/mail.py
deleted file mode 100644
index d54b387b7..000000000
--- a/src/documents/mail.py
+++ /dev/null
@@ -1,250 +0,0 @@
-import datetime
-import imaplib
-import logging
-import os
-import re
-import time
-import uuid
-
-from base64 import b64decode
-from email import policy
-from email.parser import BytesParser
-from dateutil import parser
-
-from django.conf import settings
-
-from .models import Correspondent
-
-
-class MailFetcherError(Exception):
- pass
-
-
-class InvalidMessageError(MailFetcherError):
- pass
-
-
-class Loggable(object):
-
- def __init__(self, group=None):
- self.logger = logging.getLogger(__name__)
- self.logging_group = group or uuid.uuid4()
-
- def log(self, level, message):
- getattr(self.logger, level)(message, extra={
- "group": self.logging_group
- })
-
-
-class Message(Loggable):
- """
- A crude, but simple email message class. We assume that there's a subject
- and n attachments, and that we don't care about the message body.
- """
-
- SECRET = os.getenv("PAPERLESS_EMAIL_SECRET")
-
- def __init__(self, data, group=None):
- """
- Cribbed heavily from
- https://www.ianlewis.org/en/parsing-email-attachments-python
- """
-
- Loggable.__init__(self, group=group)
-
- self.subject = None
- self.time = None
- self.attachment = None
-
- message = BytesParser(policy=policy.default).parsebytes(data)
- self.subject = str(message["Subject"]).replace("\r\n", "")
- self.body = str(message.get_body())
-
- self.check_subject()
- self.check_body()
-
- self._set_time(message)
-
- self.log("info", 'Importing email: "{}"'.format(self.subject))
-
- attachments = []
- for part in message.walk():
-
- content_disposition = part.get("Content-Disposition")
- if not content_disposition:
- continue
-
- dispositions = content_disposition.strip().split(";")
- if len(dispositions) < 2:
- continue
-
- if not dispositions[0].lower() == "attachment" and \
- "filename" not in dispositions[1].lower():
- continue
-
- file_data = part.get_payload()
-
- attachments.append(Attachment(
- b64decode(file_data), content_type=part.get_content_type()))
-
- if len(attachments) == 0:
- raise InvalidMessageError(
- "There don't appear to be any attachments to this message")
-
- if len(attachments) > 1:
- raise InvalidMessageError(
- "There's more than one attachment to this message. It cannot "
- "be indexed automatically."
- )
-
- self.attachment = attachments[0]
-
- def __bool__(self):
- return bool(self.attachment)
-
- def check_subject(self):
- if self.subject is None:
- raise InvalidMessageError("Message does not have a subject")
- if not Correspondent.SAFE_REGEX.match(self.subject):
- raise InvalidMessageError("Message subject is unsafe: {}".format(
- self.subject))
-
- def check_body(self):
- if self.SECRET not in self.body:
- raise InvalidMessageError("The secret wasn't in the body")
-
- def _set_time(self, message):
- self.time = datetime.datetime.now()
- message_time = message.get("Date")
- if message_time:
- try:
- self.time = parser.parse(message_time)
- except (ValueError, AttributeError):
- pass # We assume that "now" is ok
-
- @property
- def file_name(self):
- return "{}.{}".format(self.subject, self.attachment.suffix)
-
-
-class Attachment(object):
-
- SAFE_SUFFIX_REGEX = re.compile(
- r"^(application/(pdf))|(image/(png|jpeg|gif|tiff))$")
-
- def __init__(self, data, content_type):
-
- self.content_type = content_type
- self.data = data
- self.suffix = None
-
- m = self.SAFE_SUFFIX_REGEX.match(self.content_type)
- if not m:
- raise MailFetcherError(
- "Not-awesome file type: {}".format(self.content_type))
- self.suffix = m.group(2) or m.group(4)
-
- def read(self):
- return self.data
-
-
-class MailFetcher(Loggable):
-
- def __init__(self, consume=settings.CONSUMPTION_DIR):
-
- Loggable.__init__(self)
-
- self._connection = None
- self._host = os.getenv("PAPERLESS_CONSUME_MAIL_HOST")
- self._port = os.getenv("PAPERLESS_CONSUME_MAIL_PORT")
- self._username = os.getenv("PAPERLESS_CONSUME_MAIL_USER")
- self._password = os.getenv("PAPERLESS_CONSUME_MAIL_PASS")
- self._inbox = os.getenv("PAPERLESS_CONSUME_MAIL_INBOX", "INBOX")
-
- self._enabled = bool(self._host)
- if self._enabled and Message.SECRET is None:
- raise MailFetcherError("No PAPERLESS_EMAIL_SECRET defined")
-
- self.last_checked = time.time()
- self.consume = consume
-
- def pull(self):
- """
- Fetch all available mail at the target address and store it locally in
- the consumption directory so that the file consumer can pick it up and
- do its thing.
- """
-
- if self._enabled:
-
- # Reset the grouping id for each fetch
- self.logging_group = uuid.uuid4()
-
- self.log("debug", "Checking mail")
-
- for message in self._get_messages():
-
- self.log("info", 'Storing email: "{}"'.format(message.subject))
-
- t = int(time.mktime(message.time.timetuple()))
- file_name = os.path.join(self.consume, message.file_name)
- with open(file_name, "wb") as f:
- f.write(message.attachment.data)
- os.utime(file_name, times=(t, t))
-
- self.last_checked = time.time()
-
- def _get_messages(self):
-
- r = []
- try:
-
- self._connect()
- self._login()
-
- for message in self._fetch():
- if message:
- r.append(message)
-
- self._connection.expunge()
- self._connection.close()
- self._connection.logout()
-
- except MailFetcherError as e:
- self.log("error", str(e))
-
- return r
-
- def _connect(self):
- try:
- self._connection = imaplib.IMAP4_SSL(self._host, self._port)
- except OSError as e:
- msg = "Problem connecting to {}: {}".format(self._host, e.strerror)
- raise MailFetcherError(msg)
-
- def _login(self):
-
- login = self._connection.login(self._username, self._password)
- if not login[0] == "OK":
- raise MailFetcherError("Can't log into mail: {}".format(login[1]))
-
- inbox = self._connection.select(self._inbox)
- if not inbox[0] == "OK":
- raise MailFetcherError("Can't find the inbox: {}".format(inbox[1]))
-
- def _fetch(self):
-
- for num in self._connection.search(None, "ALL")[1][0].split():
-
- __, data = self._connection.fetch(num, "(RFC822)")
-
- message = None
- try:
- message = Message(data[0][1], self.logging_group)
- except InvalidMessageError as e:
- self.log("error", str(e))
- else:
- self._connection.store(num, "+FLAGS", "\\Deleted")
-
- if message:
- yield message
diff --git a/src/documents/management/commands/change_storage_type.py b/src/documents/management/commands/decrypt_documents.py
similarity index 51%
rename from src/documents/management/commands/change_storage_type.py
rename to src/documents/management/commands/decrypt_documents.py
index 344d3388d..8f5c2e123 100644
--- a/src/documents/management/commands/change_storage_type.py
+++ b/src/documents/management/commands/decrypt_documents.py
@@ -2,7 +2,6 @@ import os
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
-from termcolor import colored as coloured
from documents.models import Document
from paperless.db import GnuPG
@@ -17,16 +16,6 @@ class Command(BaseCommand):
def add_arguments(self, parser):
- parser.add_argument(
- "from",
- choices=("gpg", "unencrypted"),
- help="The state you want to change your documents from"
- )
- parser.add_argument(
- "to",
- choices=("gpg", "unencrypted"),
- help="The state you want to change your documents to"
- )
parser.add_argument(
"--passphrase",
help="If PAPERLESS_PASSPHRASE isn't set already, you need to "
@@ -36,25 +25,18 @@ class Command(BaseCommand):
def handle(self, *args, **options):
try:
- print(coloured(
+ print(
"\n\nWARNING: This script is going to work directly on your "
"document originals, so\nWARNING: you probably shouldn't run "
"this unless you've got a recent backup\nWARNING: handy. It "
"*should* work without a hitch, but be safe and backup your\n"
"WARNING: stuff first.\n\nHit Ctrl+C to exit now, or Enter to "
- "continue.\n\n",
- "yellow",
- attrs=("bold",)
- ))
+ "continue.\n\n"
+ )
__ = input()
except KeyboardInterrupt:
return
- if options["from"] == options["to"]:
- raise CommandError(
- 'The "from" and "to" values can\'t be the same.'
- )
-
passphrase = options["passphrase"] or settings.PASSPHRASE
if not passphrase:
raise CommandError(
@@ -62,10 +44,7 @@ class Command(BaseCommand):
"by declaring it in your environment or your config."
)
- if options["from"] == "gpg" and options["to"] == "unencrypted":
- self.__gpg_to_unencrypted(passphrase)
- elif options["from"] == "unencrypted" and options["to"] == "gpg":
- self.__unencrypted_to_gpg(passphrase)
+ self.__gpg_to_unencrypted(passphrase)
@staticmethod
def __gpg_to_unencrypted(passphrase):
@@ -75,46 +54,33 @@ class Command(BaseCommand):
for document in encrypted_files:
- print(coloured("Decrypting {}".format(
- document).encode('utf-8'), "green"))
+ print("Decrypting {}".format(
+ document).encode('utf-8'))
old_paths = [document.source_path, document.thumbnail_path]
+
raw_document = GnuPG.decrypted(document.source_file, passphrase)
raw_thumb = GnuPG.decrypted(document.thumbnail_file, passphrase)
document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
+ ext = os.path.splitext(document.filename)[1]
+
+ if not ext == '.gpg':
+ raise CommandError(
+ f"Abort: encrypted file {document.source_path} does not "
+ f"end with .gpg")
+
+ document.filename = os.path.splitext(document.filename)[0]
+
with open(document.source_path, "wb") as f:
f.write(raw_document)
with open(document.thumbnail_path, "wb") as f:
f.write(raw_thumb)
- document.save(update_fields=("storage_type",))
-
- for path in old_paths:
- os.unlink(path)
-
- @staticmethod
- def __unencrypted_to_gpg(passphrase):
-
- unencrypted_files = Document.objects.filter(
- storage_type=Document.STORAGE_TYPE_UNENCRYPTED)
-
- for document in unencrypted_files:
-
- print(coloured("Encrypting {}".format(document), "green"))
-
- old_paths = [document.source_path, document.thumbnail_path]
- with open(document.source_path, "rb") as raw_document:
- with open(document.thumbnail_path, "rb") as raw_thumb:
- document.storage_type = Document.STORAGE_TYPE_GPG
- with open(document.source_path, "wb") as f:
- f.write(GnuPG.encrypted(raw_document, passphrase))
- with open(document.thumbnail_path, "wb") as f:
- f.write(GnuPG.encrypted(raw_thumb, passphrase))
-
- document.save(update_fields=("storage_type",))
+ Document.objects.filter(id=document.id).update(
+ storage_type=document.storage_type, filename=document.filename)
for path in old_paths:
os.unlink(path)
diff --git a/src/documents/management/commands/document_archiver.py b/src/documents/management/commands/document_archiver.py
new file mode 100644
index 000000000..7b9a123d9
--- /dev/null
+++ b/src/documents/management/commands/document_archiver.py
@@ -0,0 +1,128 @@
+import hashlib
+import multiprocessing
+
+import logging
+import os
+import shutil
+import uuid
+
+import tqdm
+from django import db
+from django.conf import settings
+from django.core.management.base import BaseCommand
+from django.db import transaction
+from whoosh.writing import AsyncWriter
+
+from documents.models import Document
+from ... import index
+from ...file_handling import create_source_path_directory
+from ...mixins import Renderable
+from ...parsers import get_parser_class_for_mime_type
+
+
+logger = logging.getLogger(__name__)
+
+
+def handle_document(document_id):
+ document = Document.objects.get(id=document_id)
+
+ mime_type = document.mime_type
+
+ parser_class = get_parser_class_for_mime_type(mime_type)
+
+ parser = parser_class(logging_group=uuid.uuid4())
+
+ try:
+ parser.parse(document.source_path, mime_type)
+
+ if parser.get_archive_path():
+ with transaction.atomic():
+ with open(parser.get_archive_path(), 'rb') as f:
+ checksum = hashlib.md5(f.read()).hexdigest()
+ # i'm going to save first so that in case the file move
+ # fails, the database is rolled back.
+ # we also don't use save() since that triggers the filehandling
+ # logic, and we don't want that yet (file not yet in place)
+ Document.objects.filter(pk=document.pk).update(
+ archive_checksum=checksum,
+ content=parser.get_text()
+ )
+ create_source_path_directory(document.archive_path)
+ shutil.move(parser.get_archive_path(), document.archive_path)
+
+ with AsyncWriter(index.open_index()) as writer:
+ index.update_document(writer, document)
+
+ except Exception as e:
+ logger.error(f"Error while parsing document {document}: {str(e)}")
+ finally:
+ parser.cleanup()
+
+
+class Command(Renderable, BaseCommand):
+
+ help = """
+ Using the current classification model, assigns correspondents, tags
+ and document types to all documents, effectively allowing you to
+ back-tag all previously indexed documents with metadata created (or
+ modified) after their initial import.
+ """.replace(" ", "")
+
+ def __init__(self, *args, **kwargs):
+ self.verbosity = 0
+ BaseCommand.__init__(self, *args, **kwargs)
+
+ def add_arguments(self, parser):
+ parser.add_argument(
+ "-f", "--overwrite",
+ default=False,
+ action="store_true",
+ help="Recreates the archived document for documents that already "
+ "have an archived version."
+ )
+ parser.add_argument(
+ "-d", "--document",
+ default=None,
+ type=int,
+ required=False,
+ help="Specify the ID of a document, and this command will only "
+ "run on this specific document."
+ )
+
+ def handle(self, *args, **options):
+
+ os.makedirs(settings.SCRATCH_DIR, exist_ok=True)
+
+ overwrite = options["overwrite"]
+
+ if options['document']:
+ documents = Document.objects.filter(pk=options['document'])
+ else:
+ documents = Document.objects.all()
+
+ document_ids = list(map(
+ lambda doc: doc.id,
+ filter(
+ lambda d: overwrite or not d.archive_checksum,
+ documents
+ )
+ ))
+
+ # Note to future self: this prevents django from reusing database
+ # conncetions between processes, which is bad and does not work
+ # with postgres.
+ db.connections.close_all()
+
+ try:
+
+ logging.getLogger().handlers[0].level = logging.ERROR
+ with multiprocessing.Pool(processes=settings.TASK_WORKERS) as pool:
+ list(tqdm.tqdm(
+ pool.imap_unordered(
+ handle_document,
+ document_ids
+ ),
+ total=len(document_ids)
+ ))
+ except KeyboardInterrupt:
+ print("Aborting...")
diff --git a/src/documents/management/commands/document_consumer.py b/src/documents/management/commands/document_consumer.py
index 8a6ccfcdd..8ac60aa6d 100644
--- a/src/documents/management/commands/document_consumer.py
+++ b/src/documents/management/commands/document_consumer.py
@@ -1,38 +1,120 @@
import logging
import os
-import time
+from pathlib import Path
+from time import sleep
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
+from django.utils.text import slugify
+from django_q.tasks import async_task
+from watchdog.events import FileSystemEventHandler
+from watchdog.observers.polling import PollingObserver
-from ...consumer import Consumer, ConsumerError
-from ...mail import MailFetcher, MailFetcherError
+from documents.models import Tag
+from documents.parsers import is_file_ext_supported
try:
- from inotify_simple import INotify, flags
+ from inotifyrecursive import INotify, flags
except ImportError:
INotify = flags = None
+logger = logging.getLogger(__name__)
+
+
+def _tags_from_path(filepath):
+ """Walk up the directory tree from filepath to CONSUMPTION_DIr
+ and get or create Tag IDs for every directory.
+ """
+ tag_ids = set()
+ path_parts = Path(filepath).relative_to(
+ settings.CONSUMPTION_DIR).parent.parts
+ for part in path_parts:
+ tag_ids.add(Tag.objects.get_or_create(name__iexact=part, defaults={
+ "name": part
+ })[0].pk)
+
+ return tag_ids
+
+
+def _consume(filepath):
+ if os.path.isdir(filepath):
+ return
+
+ if not os.path.isfile(filepath):
+ logger.debug(
+ f"Not consuming file {filepath}: File has moved.")
+ return
+
+ if not is_file_ext_supported(os.path.splitext(filepath)[1]):
+ logger.debug(
+ f"Not consuming file {filepath}: Unknown file extension.")
+ return
+
+ tag_ids = None
+ try:
+ if settings.CONSUMER_SUBDIRS_AS_TAGS:
+ tag_ids = _tags_from_path(filepath)
+ except Exception as e:
+ logger.error(
+ "Error creating tags from path: {}".format(e))
+
+ try:
+ async_task("documents.tasks.consume_file",
+ filepath,
+ override_tag_ids=tag_ids if tag_ids else None,
+ task_name=os.path.basename(filepath)[:100])
+ except Exception as e:
+ # Catch all so that the consumer won't crash.
+ # This is also what the test case is listening for to check for
+ # errors.
+ logger.error(
+ "Error while consuming document: {}".format(e))
+
+
+def _consume_wait_unmodified(file, num_tries=20, wait_time=1):
+ mtime = -1
+ current_try = 0
+ while current_try < num_tries:
+ try:
+ new_mtime = os.stat(file).st_mtime
+ except FileNotFoundError:
+ logger.debug(f"File {file} moved while waiting for it to remain "
+ f"unmodified.")
+ return
+ if new_mtime == mtime:
+ _consume(file)
+ return
+ mtime = new_mtime
+ sleep(wait_time)
+ current_try += 1
+
+ logger.error(f"Timeout while waiting on file {file} to remain unmodified.")
+
+
+class Handler(FileSystemEventHandler):
+
+ def on_created(self, event):
+ _consume_wait_unmodified(event.src_path)
+
+ def on_moved(self, event):
+ _consume_wait_unmodified(event.dest_path)
+
class Command(BaseCommand):
"""
On every iteration of an infinite loop, consume what we can from the
- consumption directory, and fetch any mail available.
+ consumption directory.
"""
- ORIGINAL_DOCS = os.path.join(settings.MEDIA_ROOT, "documents", "originals")
- THUMB_DOCS = os.path.join(settings.MEDIA_ROOT, "documents", "thumbnails")
+ # This is here primarily for the tests and is irrelevant in production.
+ stop_flag = False
def __init__(self, *args, **kwargs):
- self.verbosity = 0
self.logger = logging.getLogger(__name__)
- self.file_consumer = None
- self.mail_fetcher = None
- self.first_iteration = True
-
BaseCommand.__init__(self, *args, **kwargs)
+ self.observer = None
def add_arguments(self, parser):
parser.add_argument(
@@ -41,111 +123,81 @@ class Command(BaseCommand):
nargs="?",
help="The consumption directory."
)
- parser.add_argument(
- "--loop-time",
- default=settings.CONSUMER_LOOP_TIME,
- type=int,
- help="Wait time between each loop (in seconds)."
- )
- parser.add_argument(
- "--mail-delta",
- default=10,
- type=int,
- help="Wait time between each mail fetch (in minutes)."
- )
parser.add_argument(
"--oneshot",
action="store_true",
help="Run only once."
)
- parser.add_argument(
- "--no-inotify",
- action="store_true",
- help="Don't use inotify, even if it's available.",
- default=False
- )
def handle(self, *args, **options):
-
- self.verbosity = options["verbosity"]
directory = options["directory"]
- loop_time = options["loop_time"]
- mail_delta = options["mail_delta"] * 60
- use_inotify = INotify is not None and options["no_inotify"] is False
+ recursive = settings.CONSUMER_RECURSIVE
- try:
- self.file_consumer = Consumer(consume=directory)
- self.mail_fetcher = MailFetcher(consume=directory)
- except (ConsumerError, MailFetcherError) as e:
- raise CommandError(e)
-
- for d in (self.ORIGINAL_DOCS, self.THUMB_DOCS):
- os.makedirs(d, exist_ok=True)
-
- logging.getLogger(__name__).info(
- "Starting document consumer at {}{}".format(
- directory,
- " with inotify" if use_inotify else ""
+ if not directory:
+ raise CommandError(
+ "CONSUMPTION_DIR does not appear to be set."
)
- )
+
+ if not os.path.isdir(directory):
+ raise CommandError(
+ f"Consumption directory {directory} does not exist")
+
+ if recursive:
+ for dirpath, _, filenames in os.walk(directory):
+ for filename in filenames:
+ filepath = os.path.join(dirpath, filename)
+ _consume(filepath)
+ else:
+ for entry in os.scandir(directory):
+ _consume(entry.path)
if options["oneshot"]:
- self.loop_step(mail_delta)
+ return
+
+ if settings.CONSUMER_POLLING == 0 and INotify:
+ self.handle_inotify(directory, recursive)
else:
- try:
- if use_inotify:
- self.loop_inotify(mail_delta)
- else:
- self.loop(loop_time, mail_delta)
- except KeyboardInterrupt:
- print("Exiting")
+ self.handle_polling(directory, recursive)
- def loop(self, loop_time, mail_delta):
- while True:
- start_time = time.time()
- if self.verbosity > 1:
- print(".", int(start_time))
- self.loop_step(mail_delta, start_time)
- # Sleep until the start of the next loop step
- time.sleep(max(0, start_time + loop_time - time.time()))
+ logger.debug("Consumer exiting.")
- def loop_step(self, mail_delta, time_now=None):
+ def handle_polling(self, directory, recursive):
+ logging.getLogger(__name__).info(
+ f"Polling directory for changes: {directory}")
+ self.observer = PollingObserver(timeout=settings.CONSUMER_POLLING)
+ self.observer.schedule(Handler(), directory, recursive=recursive)
+ self.observer.start()
+ try:
+ while self.observer.is_alive():
+ self.observer.join(1)
+ if self.stop_flag:
+ self.observer.stop()
+ except KeyboardInterrupt:
+ self.observer.stop()
+ self.observer.join()
- # Occasionally fetch mail and store it to be consumed on the next loop
- # We fetch email when we first start up so that it is not necessary to
- # wait for 10 minutes after making changes to the config file.
- next_mail_time = self.mail_fetcher.last_checked + mail_delta
- if self.first_iteration or time_now > next_mail_time:
- self.first_iteration = False
- self.mail_fetcher.pull()
+ def handle_inotify(self, directory, recursive):
+ logging.getLogger(__name__).info(
+ f"Using inotify to watch directory for changes: {directory}")
- self.file_consumer.consume_new_files()
-
- def loop_inotify(self, mail_delta):
- directory = self.file_consumer.consume
inotify = INotify()
- inotify.add_watch(directory, flags.CLOSE_WRITE | flags.MOVED_TO)
+ inotify_flags = flags.CLOSE_WRITE | flags.MOVED_TO
+ if recursive:
+ descriptor = inotify.add_watch_recursive(directory, inotify_flags)
+ else:
+ descriptor = inotify.add_watch(directory, inotify_flags)
- # Run initial mail fetch and consume all currently existing documents
- self.loop_step(mail_delta)
- next_mail_time = self.mail_fetcher.last_checked + mail_delta
+ try:
+ while not self.stop_flag:
+ for event in inotify.read(timeout=1000):
+ if recursive:
+ path = inotify.get_path(event.wd)
+ else:
+ path = directory
+ filepath = os.path.join(path, event.name)
+ _consume(filepath)
+ except KeyboardInterrupt:
+ pass
- while True:
- # Consume documents until next_mail_time
- while True:
- delta = next_mail_time - time.time()
- if delta > 0:
- for event in inotify.read(timeout=delta):
- file = os.path.join(directory, event.name)
- if os.path.isfile(file):
- self.file_consumer.try_consume_file(file)
- else:
- self.logger.warning(
- "Skipping %s as it is not a file",
- file
- )
- else:
- break
-
- self.mail_fetcher.pull()
- next_mail_time = self.mail_fetcher.last_checked + mail_delta
+ inotify.rm_watch(descriptor)
+ inotify.close()
diff --git a/src/documents/management/commands/document_correspondents.py b/src/documents/management/commands/document_correspondents.py
deleted file mode 100644
index 0709c49d2..000000000
--- a/src/documents/management/commands/document_correspondents.py
+++ /dev/null
@@ -1,82 +0,0 @@
-import sys
-
-from django.core.management.base import BaseCommand
-
-from documents.models import Correspondent, Document
-
-from ...mixins import Renderable
-
-
-class Command(Renderable, BaseCommand):
-
- help = """
- Using the current set of correspondent rules, apply said rules to all
- documents in the database, effectively allowing you to back-tag all
- previously indexed documents with correspondent created (or modified)
- after their initial import.
- """.replace(" ", "")
-
- TOO_MANY_CONTINUE = (
- "Detected {} potential correspondents for {}, so we've opted for {}")
- TOO_MANY_SKIP = (
- "Detected {} potential correspondents for {}, so we're skipping it")
- CHANGE_MESSAGE = (
- 'Document {}: "{}" was given the correspondent id {}: "{}"')
-
- def __init__(self, *args, **kwargs):
- self.verbosity = 0
- BaseCommand.__init__(self, *args, **kwargs)
-
- def add_arguments(self, parser):
- parser.add_argument(
- "--use-first",
- default=False,
- action="store_true",
- help="By default this command won't try to assign a correspondent "
- "if more than one matches the document. Use this flag if "
- "you'd rather it just pick the first one it finds."
- )
-
- def handle(self, *args, **options):
-
- self.verbosity = options["verbosity"]
-
- for document in Document.objects.filter(correspondent__isnull=True):
-
- potential_correspondents = list(
- Correspondent.match_all(document.content))
-
- if not potential_correspondents:
- continue
-
- potential_count = len(potential_correspondents)
- correspondent = potential_correspondents[0]
-
- if potential_count > 1:
- if not options["use_first"]:
- print(
- self.TOO_MANY_SKIP.format(potential_count, document),
- file=sys.stderr
- )
- continue
- print(
- self.TOO_MANY_CONTINUE.format(
- potential_count,
- document,
- correspondent
- ),
- file=sys.stderr
- )
-
- document.correspondent = correspondent
- document.save(update_fields=("correspondent",))
-
- print(
- self.CHANGE_MESSAGE.format(
- document.pk,
- document.title,
- correspondent.pk,
- correspondent.name
- ),
- file=sys.stderr
- )
diff --git a/src/documents/management/commands/document_create_classifier.py b/src/documents/management/commands/document_create_classifier.py
new file mode 100755
index 000000000..fbfb7f7e6
--- /dev/null
+++ b/src/documents/management/commands/document_create_classifier.py
@@ -0,0 +1,18 @@
+from django.core.management.base import BaseCommand
+
+from ...mixins import Renderable
+from ...tasks import train_classifier
+
+
+class Command(Renderable, BaseCommand):
+
+ help = """
+ Trains the classifier on your data and saves the resulting models to a
+ file. The document consumer will then automatically use this new model.
+ """.replace(" ", "")
+
+ def __init__(self, *args, **kwargs):
+ BaseCommand.__init__(self, *args, **kwargs)
+
+ def handle(self, *args, **options):
+ train_classifier()
diff --git a/src/documents/management/commands/document_exporter.py b/src/documents/management/commands/document_exporter.py
index 42a514348..a7a17f124 100644
--- a/src/documents/management/commands/document_exporter.py
+++ b/src/documents/management/commands/document_exporter.py
@@ -1,16 +1,16 @@
import json
import os
-import time
import shutil
+import time
-from django.core.management.base import BaseCommand, CommandError
from django.core import serializers
+from django.core.management.base import BaseCommand, CommandError
-from documents.models import Document, Correspondent, Tag
+from documents.models import Document, Correspondent, Tag, DocumentType
+from documents.settings import EXPORTER_FILE_NAME, EXPORTER_THUMBNAIL_NAME, \
+ EXPORTER_ARCHIVE_NAME
from paperless.db import GnuPG
-
from ...mixins import Renderable
-from documents.settings import EXPORTER_FILE_NAME, EXPORTER_THUMBNAIL_NAME
class Command(Renderable, BaseCommand):
@@ -23,13 +23,6 @@ class Command(Renderable, BaseCommand):
def add_arguments(self, parser):
parser.add_argument("target")
- parser.add_argument(
- "--legacy",
- action="store_true",
- help="Don't try to export all of the document data, just dump the "
- "original document files out in a format that makes "
- "re-consuming them easy."
- )
def __init__(self, *args, **kwargs):
BaseCommand.__init__(self, *args, **kwargs)
@@ -45,10 +38,10 @@ class Command(Renderable, BaseCommand):
if not os.access(self.target, os.W_OK):
raise CommandError("That path doesn't appear to be writable")
- if options["legacy"]:
- self.dump_legacy()
- else:
- self.dump()
+ if os.listdir(self.target):
+ raise CommandError("That directory is not empty.")
+
+ self.dump()
def dump(self):
@@ -64,67 +57,64 @@ class Command(Renderable, BaseCommand):
document = document_map[document_dict["pk"]]
- file_target = os.path.join(self.target, document.file_name)
+ print(f"Exporting: {document}")
- thumbnail_name = document.file_name + "-thumbnail.png"
+ filename_counter = 0
+ while True:
+ original_name = document.get_public_filename(
+ counter=filename_counter)
+ original_target = os.path.join(self.target, original_name)
+
+ if not os.path.exists(original_target):
+ break
+ else:
+ filename_counter += 1
+
+ thumbnail_name = original_name + "-thumbnail.png"
thumbnail_target = os.path.join(self.target, thumbnail_name)
- document_dict[EXPORTER_FILE_NAME] = document.file_name
+ document_dict[EXPORTER_FILE_NAME] = original_name
document_dict[EXPORTER_THUMBNAIL_NAME] = thumbnail_name
- print("Exporting: {}".format(file_target))
+ if os.path.exists(document.archive_path):
+ archive_name = document.get_public_filename(
+ archive=True, counter=filename_counter, suffix="_archive")
+ archive_target = os.path.join(self.target, archive_name)
+ document_dict[EXPORTER_ARCHIVE_NAME] = archive_name
+ else:
+ archive_target = None
t = int(time.mktime(document.created.timetuple()))
if document.storage_type == Document.STORAGE_TYPE_GPG:
- with open(file_target, "wb") as f:
+ with open(original_target, "wb") as f:
f.write(GnuPG.decrypted(document.source_file))
- os.utime(file_target, times=(t, t))
+ os.utime(original_target, times=(t, t))
with open(thumbnail_target, "wb") as f:
f.write(GnuPG.decrypted(document.thumbnail_file))
os.utime(thumbnail_target, times=(t, t))
+ if archive_target:
+ with open(archive_target, "wb") as f:
+ f.write(GnuPG.decrypted(document.archive_path))
+ os.utime(archive_target, times=(t, t))
else:
- shutil.copy(document.source_path, file_target)
+ shutil.copy(document.source_path, original_target)
shutil.copy(document.thumbnail_path, thumbnail_target)
+ if archive_target:
+ shutil.copy(document.archive_path, archive_target)
+
manifest += json.loads(
serializers.serialize("json", Correspondent.objects.all()))
manifest += json.loads(serializers.serialize(
"json", Tag.objects.all()))
+ manifest += json.loads(serializers.serialize(
+ "json", DocumentType.objects.all()))
+
with open(os.path.join(self.target, "manifest.json"), "w") as f:
json.dump(manifest, f, indent=2)
-
- def dump_legacy(self):
-
- for document in Document.objects.all():
-
- target = os.path.join(
- self.target, self._get_legacy_file_name(document))
-
- print("Exporting: {}".format(target))
-
- with open(target, "wb") as f:
- f.write(GnuPG.decrypted(document.source_file))
- t = int(time.mktime(document.created.timetuple()))
- os.utime(target, times=(t, t))
-
- @staticmethod
- def _get_legacy_file_name(doc):
-
- if not doc.correspondent and not doc.title:
- return os.path.basename(doc.source_path)
-
- created = doc.created.strftime("%Y%m%d%H%M%SZ")
- tags = ",".join([t.slug for t in doc.tags.all()])
-
- if tags:
- return "{} - {} - {} - {}.{}".format(
- created, doc.correspondent, doc.title, tags, doc.file_type)
-
- return "{} - {} - {}.{}".format(
- created, doc.correspondent, doc.title, doc.file_type)
diff --git a/src/documents/management/commands/document_importer.py b/src/documents/management/commands/document_importer.py
index ae5c1853f..70d05d98b 100644
--- a/src/documents/management/commands/document_importer.py
+++ b/src/documents/management/commands/document_importer.py
@@ -3,16 +3,17 @@ import os
import shutil
from django.conf import settings
-from django.core.management.base import BaseCommand, CommandError
from django.core.management import call_command
+from django.core.management.base import BaseCommand, CommandError
+from filelock import FileLock
from documents.models import Document
-from paperless.db import GnuPG
-
+from documents.settings import EXPORTER_FILE_NAME, EXPORTER_THUMBNAIL_NAME, \
+ EXPORTER_ARCHIVE_NAME
+from ...file_handling import create_source_path_directory, \
+ generate_unique_filename
from ...mixins import Renderable
-from documents.settings import EXPORTER_FILE_NAME, EXPORTER_THUMBNAIL_NAME
-
class Command(Renderable, BaseCommand):
@@ -80,47 +81,55 @@ class Command(Renderable, BaseCommand):
'appear to be in the source directory.'.format(doc_file)
)
+ if EXPORTER_ARCHIVE_NAME in record:
+ archive_file = record[EXPORTER_ARCHIVE_NAME]
+ if not os.path.exists(os.path.join(self.source, archive_file)):
+ raise CommandError(
+ f"The manifest file refers to {archive_file} which "
+ f"does not appear to be in the source directory."
+ )
+
def _import_files_from_manifest(self):
+ os.makedirs(settings.ORIGINALS_DIR, exist_ok=True)
+ os.makedirs(settings.THUMBNAIL_DIR, exist_ok=True)
+ os.makedirs(settings.ARCHIVE_DIR, exist_ok=True)
+
for record in self.manifest:
if not record["model"] == "documents.document":
continue
- doc_file = record[EXPORTER_FILE_NAME]
- thumb_file = record[EXPORTER_THUMBNAIL_NAME]
document = Document.objects.get(pk=record["pk"])
+ doc_file = record[EXPORTER_FILE_NAME]
document_path = os.path.join(self.source, doc_file)
+
+ thumb_file = record[EXPORTER_THUMBNAIL_NAME]
thumbnail_path = os.path.join(self.source, thumb_file)
- if settings.PASSPHRASE:
-
- with open(document_path, "rb") as unencrypted:
- with open(document.source_path, "wb") as encrypted:
- print("Encrypting {} and saving it to {}".format(
- doc_file, document.source_path))
- encrypted.write(GnuPG.encrypted(unencrypted))
-
- with open(thumbnail_path, "rb") as unencrypted:
- with open(document.thumbnail_path, "wb") as encrypted:
- print("Encrypting {} and saving it to {}".format(
- thumb_file, document.thumbnail_path))
- encrypted.write(GnuPG.encrypted(unencrypted))
-
+ if EXPORTER_ARCHIVE_NAME in record:
+ archive_file = record[EXPORTER_ARCHIVE_NAME]
+ archive_path = os.path.join(self.source, archive_file)
else:
+ archive_path = None
+ document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
+
+ with FileLock(settings.MEDIA_LOCK):
+ document.filename = generate_unique_filename(
+ document, settings.ORIGINALS_DIR)
+
+ if os.path.isfile(document.source_path):
+ raise FileExistsError(document.source_path)
+
+ create_source_path_directory(document.source_path)
+
+ print(f"Moving {document_path} to {document.source_path}")
shutil.copy(document_path, document.source_path)
shutil.copy(thumbnail_path, document.thumbnail_path)
+ if archive_path:
+ create_source_path_directory(document.archive_path)
+ shutil.copy(archive_path, document.archive_path)
- # Reset the storage type to whatever we've used while importing
-
- storage_type = Document.STORAGE_TYPE_UNENCRYPTED
- if settings.PASSPHRASE:
- storage_type = Document.STORAGE_TYPE_GPG
-
- Document.objects.filter(
- pk__in=[r["pk"] for r in self.manifest]
- ).update(
- storage_type=storage_type
- )
+ document.save()
diff --git a/src/documents/management/commands/document_index.py b/src/documents/management/commands/document_index.py
new file mode 100644
index 000000000..7dfdbaa42
--- /dev/null
+++ b/src/documents/management/commands/document_index.py
@@ -0,0 +1,25 @@
+from django.core.management import BaseCommand
+
+from documents.mixins import Renderable
+from documents.tasks import index_reindex, index_optimize
+
+
+class Command(Renderable, BaseCommand):
+
+ help = "Manages the document index."
+
+ def __init__(self, *args, **kwargs):
+ self.verbosity = 0
+ BaseCommand.__init__(self, *args, **kwargs)
+
+ def add_arguments(self, parser):
+ parser.add_argument("command", choices=['reindex', 'optimize'])
+
+ def handle(self, *args, **options):
+
+ self.verbosity = options["verbosity"]
+
+ if options['command'] == 'reindex':
+ index_reindex()
+ elif options['command'] == 'optimize':
+ index_optimize()
diff --git a/src/documents/management/commands/document_logs.py b/src/documents/management/commands/document_logs.py
index 9a8271e71..06efc3850 100644
--- a/src/documents/management/commands/document_logs.py
+++ b/src/documents/management/commands/document_logs.py
@@ -8,5 +8,5 @@ class Command(BaseCommand):
help = "A quick & dirty way to see what's in the logs"
def handle(self, *args, **options):
- for l in Log.objects.order_by("pk"):
- print(l)
+ for log in Log.objects.order_by("pk"):
+ print(log)
diff --git a/src/documents/management/commands/document_renamer.py b/src/documents/management/commands/document_renamer.py
index d7d77a111..745d2d03d 100644
--- a/src/documents/management/commands/document_renamer.py
+++ b/src/documents/management/commands/document_renamer.py
@@ -1,7 +1,10 @@
+import logging
+
+import tqdm
from django.core.management.base import BaseCommand
+from django.db.models.signals import post_save
-from documents.models import Document, Tag
-
+from documents.models import Document
from ...mixins import Renderable
@@ -19,6 +22,7 @@ class Command(Renderable, BaseCommand):
self.verbosity = options["verbosity"]
- for document in Document.objects.all():
- # Saving the document again will generate a new filename and rename
- document.save()
+ logging.getLogger().handlers[0].level = logging.ERROR
+
+ for document in tqdm.tqdm(Document.objects.all()):
+ post_save.send(Document, instance=document)
diff --git a/src/documents/management/commands/document_retagger.py b/src/documents/management/commands/document_retagger.py
old mode 100644
new mode 100755
index 8f56e1eea..cf014dc6f
--- a/src/documents/management/commands/document_retagger.py
+++ b/src/documents/management/commands/document_retagger.py
@@ -1,32 +1,105 @@
+import logging
+
from django.core.management.base import BaseCommand
-from documents.models import Document, Tag
-
+from documents.classifier import DocumentClassifier, \
+ IncompatibleClassifierVersionError
+from documents.models import Document
from ...mixins import Renderable
+from ...signals.handlers import set_correspondent, set_document_type, set_tags
class Command(Renderable, BaseCommand):
help = """
- Using the current set of tagging rules, apply said rules to all
- documents in the database, effectively allowing you to back-tag all
- previously indexed documents with tags created (or modified) after
- their initial import.
+ Using the current classification model, assigns correspondents, tags
+ and document types to all documents, effectively allowing you to
+ back-tag all previously indexed documents with metadata created (or
+ modified) after their initial import.
""".replace(" ", "")
def __init__(self, *args, **kwargs):
self.verbosity = 0
BaseCommand.__init__(self, *args, **kwargs)
+ def add_arguments(self, parser):
+ parser.add_argument(
+ "-c", "--correspondent",
+ default=False,
+ action="store_true"
+ )
+ parser.add_argument(
+ "-T", "--tags",
+ default=False,
+ action="store_true"
+ )
+ parser.add_argument(
+ "-t", "--document_type",
+ default=False,
+ action="store_true"
+ )
+ parser.add_argument(
+ "-i", "--inbox-only",
+ default=False,
+ action="store_true"
+ )
+ parser.add_argument(
+ "--use-first",
+ default=False,
+ action="store_true",
+ help="By default this command won't try to assign a correspondent "
+ "if more than one matches the document. Use this flag if "
+ "you'd rather it just pick the first one it finds."
+ )
+ parser.add_argument(
+ "-f", "--overwrite",
+ default=False,
+ action="store_true",
+ help="If set, the document retagger will overwrite any previously"
+ "set correspondent, document and remove correspondents, types"
+ "and tags that do not match anymore due to changed rules."
+ )
+
def handle(self, *args, **options):
self.verbosity = options["verbosity"]
- for document in Document.objects.all():
+ if options["inbox_only"]:
+ queryset = Document.objects.filter(tags__is_inbox_tag=True)
+ else:
+ queryset = Document.objects.all()
+ documents = queryset.distinct()
- tags = Tag.objects.exclude(
- pk__in=document.tags.values_list("pk", flat=True))
+ classifier = DocumentClassifier()
+ try:
+ classifier.reload()
+ except (FileNotFoundError, IncompatibleClassifierVersionError) as e:
+ logging.getLogger(__name__).warning(
+ f"Cannot classify documents: {e}.")
+ classifier = None
- for tag in Tag.match_all(document.content, tags):
- print('Tagging {} with "{}"'.format(document, tag))
- document.tags.add(tag)
+ for document in documents:
+ logging.getLogger(__name__).info(
+ f"Processing document {document.title}")
+
+ if options['correspondent']:
+ set_correspondent(
+ sender=None,
+ document=document,
+ classifier=classifier,
+ replace=options['overwrite'],
+ use_first=options['use_first'])
+
+ if options['document_type']:
+ set_document_type(sender=None,
+ document=document,
+ classifier=classifier,
+ replace=options['overwrite'],
+ use_first=options['use_first'])
+
+ if options['tags']:
+ set_tags(
+ sender=None,
+ document=document,
+ classifier=classifier,
+ replace=options['overwrite'])
diff --git a/src/documents/managers.py b/src/documents/managers.py
deleted file mode 100644
index f324137ef..000000000
--- a/src/documents/managers.py
+++ /dev/null
@@ -1,70 +0,0 @@
-from django.conf import settings
-
-from django.db import models
-from django.db.models.aggregates import Max
-
-
-class GroupConcat(models.Aggregate):
- """
- Theoretically, this should work in Sqlite, PostgreSQL, and MySQL, but I've
- only ever tested it in Sqlite.
- """
-
- ENGINE_SQLITE = 1
- ENGINE_POSTGRESQL = 2
- ENGINE_MYSQL = 3
- ENGINES = {
- "django.db.backends.sqlite3": ENGINE_SQLITE,
- "django.db.backends.postgresql_psycopg2": ENGINE_POSTGRESQL,
- "django.db.backends.postgresql": ENGINE_POSTGRESQL,
- "django.db.backends.mysql": ENGINE_MYSQL
- }
-
- def __init__(self, expression, separator="\n", **extra):
-
- self.engine = self._get_engine()
- self.function = self._get_function()
- self.template = self._get_template(separator)
-
- models.Aggregate.__init__(
- self,
- expression,
- output_field=models.CharField(),
- **extra
- )
-
- def _get_engine(self):
- engine = settings.DATABASES["default"]["ENGINE"]
- try:
- return self.ENGINES[engine]
- except KeyError:
- raise NotImplementedError(
- "There's currently no support for {} when it comes to group "
- "concatenation in Paperless".format(engine)
- )
-
- def _get_function(self):
- if self.engine == self.ENGINE_POSTGRESQL:
- return "STRING_AGG"
- return "GROUP_CONCAT"
-
- def _get_template(self, separator):
- if self.engine == self.ENGINE_MYSQL:
- return "%(function)s(%(expressions)s SEPARATOR '{}')".format(
- separator)
- return "%(function)s(%(expressions)s, '{}')".format(separator)
-
-
-class LogQuerySet(models.query.QuerySet):
-
- def by_group(self):
- return self.values("group").annotate(
- time=Max("modified"),
- messages=GroupConcat("message"),
- ).order_by("-time")
-
-
-class LogManager(models.Manager):
-
- def get_queryset(self):
- return LogQuerySet(self.model, using=self._db)
diff --git a/src/documents/matching.py b/src/documents/matching.py
new file mode 100644
index 000000000..212698ad3
--- /dev/null
+++ b/src/documents/matching.py
@@ -0,0 +1,118 @@
+import re
+
+from fuzzywuzzy import fuzz
+
+from documents.models import MatchingModel, Correspondent, DocumentType, Tag
+
+
+def match_correspondents(document_content, classifier):
+ if classifier:
+ pred_id = classifier.predict_correspondent(document_content)
+ else:
+ pred_id = None
+
+ correspondents = Correspondent.objects.all()
+
+ return list(filter(
+ lambda o: matches(o, document_content) or o.pk == pred_id,
+ correspondents))
+
+
+def match_document_types(document_content, classifier):
+ if classifier:
+ pred_id = classifier.predict_document_type(document_content)
+ else:
+ pred_id = None
+
+ document_types = DocumentType.objects.all()
+
+ return list(filter(
+ lambda o: matches(o, document_content) or o.pk == pred_id,
+ document_types))
+
+
+def match_tags(document_content, classifier):
+ if classifier:
+ predicted_tag_ids = classifier.predict_tags(document_content)
+ else:
+ predicted_tag_ids = []
+
+ tags = Tag.objects.all()
+
+ return list(filter(
+ lambda o: matches(o, document_content) or o.pk in predicted_tag_ids,
+ tags))
+
+
+def matches(matching_model, document_content):
+ search_kwargs = {}
+
+ document_content = document_content.lower()
+
+ # Check that match is not empty
+ if matching_model.match.strip() == "":
+ return False
+
+ if matching_model.is_insensitive:
+ search_kwargs = {"flags": re.IGNORECASE}
+
+ if matching_model.matching_algorithm == MatchingModel.MATCH_ALL:
+ for word in _split_match(matching_model):
+ search_result = re.search(
+ rf"\b{word}\b", document_content, **search_kwargs)
+ if not search_result:
+ return False
+ return True
+
+ elif matching_model.matching_algorithm == MatchingModel.MATCH_ANY:
+ for word in _split_match(matching_model):
+ if re.search(rf"\b{word}\b", document_content, **search_kwargs):
+ return True
+ return False
+
+ elif matching_model.matching_algorithm == MatchingModel.MATCH_LITERAL:
+ return bool(re.search(
+ rf"\b{matching_model.match}\b",
+ document_content,
+ **search_kwargs
+ ))
+
+ elif matching_model.matching_algorithm == MatchingModel.MATCH_REGEX:
+ return bool(re.search(
+ re.compile(matching_model.match, **search_kwargs),
+ document_content
+ ))
+
+ elif matching_model.matching_algorithm == MatchingModel.MATCH_FUZZY:
+ match = re.sub(r'[^\w\s]', '', matching_model.match)
+ text = re.sub(r'[^\w\s]', '', document_content)
+ if matching_model.is_insensitive:
+ match = match.lower()
+ text = text.lower()
+
+ return fuzz.partial_ratio(match, text) >= 90
+
+ elif matching_model.matching_algorithm == MatchingModel.MATCH_AUTO:
+ # this is done elsewhere.
+ return False
+
+ else:
+ raise NotImplementedError("Unsupported matching algorithm")
+
+
+def _split_match(matching_model):
+ """
+ Splits the match to individual keywords, getting rid of unnecessary
+ spaces and grouping quoted words together.
+
+ Example:
+ ' some random words "with quotes " and spaces'
+ ==>
+ ["some", "random", "words", "with+quotes", "and", "spaces"]
+ """
+ findterms = re.compile(r'"([^"]+)"|(\S+)').findall
+ normspace = re.compile(r"\s+").sub
+ return [
+ normspace(" ", (t[0] or t[1]).strip()).replace(" ", r"\s+")
+ for t in findterms(matching_model.match)
+ ]
diff --git a/src/documents/migrations/1000_update_paperless_all.py b/src/documents/migrations/1000_update_paperless_all.py
new file mode 100644
index 000000000..f3fbbb6c1
--- /dev/null
+++ b/src/documents/migrations/1000_update_paperless_all.py
@@ -0,0 +1,85 @@
+# Generated by Django 3.1.3 on 2020-11-07 12:35
+import uuid
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+def logs_set_default_group(apps, schema_editor):
+ Log = apps.get_model('documents', 'Log')
+ for log in Log.objects.all():
+ if log.group is None:
+ log.group = uuid.uuid4()
+ log.save()
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('documents', '0023_document_current_filename'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='document',
+ name='archive_serial_number',
+ field=models.IntegerField(blank=True, db_index=True, help_text='The position of this document in your physical document archive.', null=True, unique=True),
+ ),
+ migrations.AddField(
+ model_name='tag',
+ name='is_inbox_tag',
+ field=models.BooleanField(default=False, help_text='Marks this tag as an inbox tag: All newly consumed documents will be tagged with inbox tags.'),
+ ),
+ migrations.CreateModel(
+ name='DocumentType',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=128, unique=True)),
+ ('slug', models.SlugField(blank=True, editable=False)),
+ ('match', models.CharField(blank=True, max_length=256)),
+ ('matching_algorithm', models.PositiveIntegerField(choices=[(1, 'Any'), (2, 'All'), (3, 'Literal'), (4, 'Regular Expression'), (5, 'Fuzzy Match'), (6, 'Automatic Classification')], default=1, help_text='Which algorithm you want to use when matching text to the OCR\'d PDF. Here, "any" looks for any occurrence of any word provided in the PDF, while "all" requires that every word provided appear in the PDF, albeit not in the order provided. A "literal" match means that the text you enter must appear in the PDF exactly as you\'ve entered it, and "regular expression" uses a regex to match the PDF. (If you don\'t know what a regex is, you probably don\'t want this option.) Finally, a "fuzzy match" looks for words or phrases that are mostly—but not exactly—the same, which can be useful for matching against documents containg imperfections that foil accurate OCR.')),
+ ('is_insensitive', models.BooleanField(default=True)),
+ ],
+ options={
+ 'abstract': False,
+ 'ordering': ('name',),
+ },
+ ),
+ migrations.AddField(
+ model_name='document',
+ name='document_type',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='documents', to='documents.documenttype'),
+ ),
+ migrations.AlterField(
+ model_name='correspondent',
+ name='matching_algorithm',
+ field=models.PositiveIntegerField(choices=[(1, 'Any'), (2, 'All'), (3, 'Literal'), (4, 'Regular Expression'), (5, 'Fuzzy Match'), (6, 'Automatic Classification')], default=1, help_text='Which algorithm you want to use when matching text to the OCR\'d PDF. Here, "any" looks for any occurrence of any word provided in the PDF, while "all" requires that every word provided appear in the PDF, albeit not in the order provided. A "literal" match means that the text you enter must appear in the PDF exactly as you\'ve entered it, and "regular expression" uses a regex to match the PDF. (If you don\'t know what a regex is, you probably don\'t want this option.) Finally, a "fuzzy match" looks for words or phrases that are mostly—but not exactly—the same, which can be useful for matching against documents containg imperfections that foil accurate OCR.'),
+ ),
+ migrations.AlterField(
+ model_name='tag',
+ name='matching_algorithm',
+ field=models.PositiveIntegerField(choices=[(1, 'Any'), (2, 'All'), (3, 'Literal'), (4, 'Regular Expression'), (5, 'Fuzzy Match'), (6, 'Automatic Classification')], default=1, help_text='Which algorithm you want to use when matching text to the OCR\'d PDF. Here, "any" looks for any occurrence of any word provided in the PDF, while "all" requires that every word provided appear in the PDF, albeit not in the order provided. A "literal" match means that the text you enter must appear in the PDF exactly as you\'ve entered it, and "regular expression" uses a regex to match the PDF. (If you don\'t know what a regex is, you probably don\'t want this option.) Finally, a "fuzzy match" looks for words or phrases that are mostly—but not exactly—the same, which can be useful for matching against documents containg imperfections that foil accurate OCR.'),
+ ),
+ migrations.AlterField(
+ model_name='document',
+ name='content',
+ field=models.TextField(blank=True, help_text='The raw, text-only data of the document. This field is primarily used for searching.'),
+ ),
+ migrations.AlterModelOptions(
+ name='log',
+ options={'ordering': ('-created',)},
+ ),
+ migrations.RemoveField(
+ model_name='log',
+ name='modified',
+ ),
+ migrations.AlterField(
+ model_name='log',
+ name='group',
+ field=models.UUIDField(blank=True, null=True),
+ ),
+ migrations.RunPython(
+ code=django.db.migrations.operations.special.RunPython.noop,
+ reverse_code=logs_set_default_group
+ ),
+ ]
diff --git a/src/documents/migrations/1001_auto_20201109_1636.py b/src/documents/migrations/1001_auto_20201109_1636.py
new file mode 100644
index 000000000..90cb53d4b
--- /dev/null
+++ b/src/documents/migrations/1001_auto_20201109_1636.py
@@ -0,0 +1,28 @@
+# Generated by Django 3.1.3 on 2020-11-09 16:36
+
+from django.db import migrations
+from django.db.migrations import RunPython
+from django_q.models import Schedule
+from django_q.tasks import schedule
+
+
+def add_schedules(apps, schema_editor):
+ schedule('documents.tasks.train_classifier', name="Train the classifier", schedule_type=Schedule.HOURLY)
+ schedule('documents.tasks.index_optimize', name="Optimize the index", schedule_type=Schedule.DAILY)
+
+
+def remove_schedules(apps, schema_editor):
+ Schedule.objects.filter(func='documents.tasks.train_classifier').delete()
+ Schedule.objects.filter(func='documents.tasks.index_optimize').delete()
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('documents', '1000_update_paperless_all'),
+ ('django_q', '0013_task_attempt_count'),
+ ]
+
+ operations = [
+ RunPython(add_schedules, remove_schedules)
+ ]
diff --git a/src/documents/migrations/1002_auto_20201111_1105.py b/src/documents/migrations/1002_auto_20201111_1105.py
new file mode 100644
index 000000000..7f6bae50b
--- /dev/null
+++ b/src/documents/migrations/1002_auto_20201111_1105.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.1.3 on 2020-11-11 11:05
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('documents', '1001_auto_20201109_1636'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='document',
+ name='filename',
+ field=models.FilePathField(default=None, editable=False, help_text='Current filename in storage', max_length=1024, null=True),
+ ),
+ ]
diff --git a/src/documents/migrations/1003_mime_types.py b/src/documents/migrations/1003_mime_types.py
new file mode 100644
index 000000000..78ecced2b
--- /dev/null
+++ b/src/documents/migrations/1003_mime_types.py
@@ -0,0 +1,90 @@
+# Generated by Django 3.1.3 on 2020-11-20 11:21
+import mimetypes
+import os
+
+import magic
+from django.conf import settings
+from django.db import migrations, models
+
+from paperless.db import GnuPG
+
+STORAGE_TYPE_UNENCRYPTED = "unencrypted"
+STORAGE_TYPE_GPG = "gpg"
+
+def source_path(self):
+ if self.filename:
+ fname = str(self.filename)
+ else:
+ fname = "{:07}.{}".format(self.pk, self.file_type)
+ if self.storage_type == STORAGE_TYPE_GPG:
+ fname += ".gpg"
+
+ return os.path.join(
+ settings.ORIGINALS_DIR,
+ fname
+ )
+
+
+def add_mime_types(apps, schema_editor):
+ Document = apps.get_model("documents", "Document")
+ documents = Document.objects.all()
+
+ for d in documents:
+ f = open(source_path(d), "rb")
+ if d.storage_type == STORAGE_TYPE_GPG:
+
+ data = GnuPG.decrypted(f)
+ else:
+ data = f.read(1024)
+
+ d.mime_type = magic.from_buffer(data, mime=True)
+ d.save()
+
+ f.close()
+
+
+def add_file_extensions(apps, schema_editor):
+ Document = apps.get_model("documents", "Document")
+ documents = Document.objects.all()
+
+ for d in documents:
+ d.file_type = os.path.splitext(d.filename)[1].strip('.')
+ d.save()
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('documents', '1002_auto_20201111_1105'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='document',
+ name='mime_type',
+ field=models.CharField(default="-", editable=False, max_length=256),
+ preserve_default=False,
+ ),
+ migrations.RunPython(add_mime_types, migrations.RunPython.noop),
+
+ # This operation is here so that we can revert the entire migration:
+ # By allowing this field to be blank and null, we can revert the
+ # remove operation further down and the database won't complain about
+ # NOT NULL violations.
+ migrations.AlterField(
+ model_name='document',
+ name='file_type',
+ field=models.CharField(
+ choices=[('pdf', 'PDF'), ('png', 'PNG'), ('jpg', 'JPG'), ('gif', 'GIF'), ('tiff', 'TIFF'), ('txt', 'TXT'), ('csv', 'CSV'), ('md', 'MD')],
+ editable=False,
+ max_length=4,
+ null=True,
+ blank=True
+ ),
+ ),
+ migrations.RunPython(migrations.RunPython.noop, add_file_extensions),
+ migrations.RemoveField(
+ model_name='document',
+ name='file_type',
+ ),
+ ]
diff --git a/src/documents/migrations/1004_sanity_check_schedule.py b/src/documents/migrations/1004_sanity_check_schedule.py
new file mode 100644
index 000000000..b6346d479
--- /dev/null
+++ b/src/documents/migrations/1004_sanity_check_schedule.py
@@ -0,0 +1,26 @@
+# Generated by Django 3.1.3 on 2020-11-25 14:53
+
+from django.db import migrations
+from django.db.migrations import RunPython
+from django_q.models import Schedule
+from django_q.tasks import schedule
+
+
+def add_schedules(apps, schema_editor):
+ schedule('documents.tasks.sanity_check', name="Perform sanity check", schedule_type=Schedule.WEEKLY)
+
+
+def remove_schedules(apps, schema_editor):
+ Schedule.objects.filter(func='documents.tasks.sanity_check').delete()
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('documents', '1003_mime_types'),
+ ('django_q', '0013_task_attempt_count'),
+ ]
+
+ operations = [
+ RunPython(add_schedules, remove_schedules)
+ ]
diff --git a/src/documents/migrations/1005_checksums.py b/src/documents/migrations/1005_checksums.py
new file mode 100644
index 000000000..401de2e1d
--- /dev/null
+++ b/src/documents/migrations/1005_checksums.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.1.3 on 2020-11-29 00:48
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('documents', '1004_sanity_check_schedule'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='document',
+ name='archive_checksum',
+ field=models.CharField(blank=True, editable=False, help_text='The checksum of the archived document.', max_length=32, null=True),
+ ),
+ migrations.AlterField(
+ model_name='document',
+ name='checksum',
+ field=models.CharField(editable=False, help_text='The checksum of the original document.', max_length=32, unique=True),
+ ),
+ ]
diff --git a/src/documents/migrations/1006_auto_20201208_2209.py b/src/documents/migrations/1006_auto_20201208_2209.py
new file mode 100644
index 000000000..49f8c8dfe
--- /dev/null
+++ b/src/documents/migrations/1006_auto_20201208_2209.py
@@ -0,0 +1,25 @@
+# Generated by Django 3.1.4 on 2020-12-08 22:09
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('documents', '1005_checksums'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='correspondent',
+ name='slug',
+ ),
+ migrations.RemoveField(
+ model_name='documenttype',
+ name='slug',
+ ),
+ migrations.RemoveField(
+ model_name='tag',
+ name='slug',
+ ),
+ ]
diff --git a/src/documents/migrations/1007_savedview_savedviewfilterrule.py b/src/documents/migrations/1007_savedview_savedviewfilterrule.py
new file mode 100644
index 000000000..664def5f1
--- /dev/null
+++ b/src/documents/migrations/1007_savedview_savedviewfilterrule.py
@@ -0,0 +1,37 @@
+# Generated by Django 3.1.4 on 2020-12-12 14:41
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('documents', '1006_auto_20201208_2209'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='SavedView',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=128)),
+ ('show_on_dashboard', models.BooleanField()),
+ ('show_in_sidebar', models.BooleanField()),
+ ('sort_field', models.CharField(max_length=128)),
+ ('sort_reverse', models.BooleanField(default=False)),
+ ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='SavedViewFilterRule',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('rule_type', models.PositiveIntegerField(choices=[(0, 'Title contains'), (1, 'Content contains'), (2, 'ASN is'), (3, 'Correspondent is'), (4, 'Document type is'), (5, 'Is in inbox'), (6, 'Has tag'), (7, 'Has any tag'), (8, 'Created before'), (9, 'Created after'), (10, 'Created year is'), (11, 'Created month is'), (12, 'Created day is'), (13, 'Added before'), (14, 'Added after'), (15, 'Modified before'), (16, 'Modified after'), (17, 'Does not have tag')])),
+ ('value', models.CharField(max_length=128)),
+ ('saved_view', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='filter_rules', to='documents.savedview')),
+ ],
+ ),
+ ]
diff --git a/src/documents/migrations/1008_auto_20201216_1736.py b/src/documents/migrations/1008_auto_20201216_1736.py
new file mode 100644
index 000000000..d94f4767f
--- /dev/null
+++ b/src/documents/migrations/1008_auto_20201216_1736.py
@@ -0,0 +1,34 @@
+# Generated by Django 3.1.4 on 2020-12-16 17:36
+
+from django.db import migrations
+import django.db.models.functions.text
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('documents', '1007_savedview_savedviewfilterrule'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='correspondent',
+ options={'ordering': (django.db.models.functions.text.Lower('name'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='document',
+ options={'ordering': ('-created',)},
+ ),
+ migrations.AlterModelOptions(
+ name='documenttype',
+ options={'ordering': (django.db.models.functions.text.Lower('name'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='savedview',
+ options={'ordering': (django.db.models.functions.text.Lower('name'),)},
+ ),
+ migrations.AlterModelOptions(
+ name='tag',
+ options={'ordering': (django.db.models.functions.text.Lower('name'),)},
+ ),
+ ]
diff --git a/src/documents/migrations/1009_auto_20201216_2005.py b/src/documents/migrations/1009_auto_20201216_2005.py
new file mode 100644
index 000000000..5e8302bb0
--- /dev/null
+++ b/src/documents/migrations/1009_auto_20201216_2005.py
@@ -0,0 +1,29 @@
+# Generated by Django 3.1.4 on 2020-12-16 20:05
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('documents', '1008_auto_20201216_1736'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='correspondent',
+ options={'ordering': ('name',)},
+ ),
+ migrations.AlterModelOptions(
+ name='documenttype',
+ options={'ordering': ('name',)},
+ ),
+ migrations.AlterModelOptions(
+ name='savedview',
+ options={'ordering': ('name',)},
+ ),
+ migrations.AlterModelOptions(
+ name='tag',
+ options={'ordering': ('name',)},
+ ),
+ ]
diff --git a/src/documents/mixins.py b/src/documents/mixins.py
old mode 100644
new mode 100755
diff --git a/src/documents/models.py b/src/documents/models.py
old mode 100644
new mode 100755
index f051e3062..3a6d155ed
--- a/src/documents/models.py
+++ b/src/documents/models.py
@@ -1,27 +1,20 @@
# coding=utf-8
-
+import datetime
import logging
import os
import re
-import uuid
from collections import OrderedDict
+import pathvalidate
+
import dateutil.parser
-from django.dispatch import receiver
from django.conf import settings
+from django.contrib.auth.models import User
from django.db import models
-from django.template.defaultfilters import slugify
from django.utils import timezone
-from django.utils.text import slugify
-from fuzzywuzzy import fuzz
-from collections import defaultdict
-from .managers import LogManager
-
-try:
- from django.core.urlresolvers import reverse
-except ImportError:
- from django.urls import reverse
+from documents.file_handling import archive_name_from_filename
+from documents.parsers import get_default_file_extension
class MatchingModel(models.Model):
@@ -31,16 +24,18 @@ class MatchingModel(models.Model):
MATCH_LITERAL = 3
MATCH_REGEX = 4
MATCH_FUZZY = 5
+ MATCH_AUTO = 6
+
MATCHING_ALGORITHMS = (
(MATCH_ANY, "Any"),
(MATCH_ALL, "All"),
(MATCH_LITERAL, "Literal"),
(MATCH_REGEX, "Regular Expression"),
(MATCH_FUZZY, "Fuzzy Match"),
+ (MATCH_AUTO, "Automatic Classification"),
)
name = models.CharField(max_length=128, unique=True)
- slug = models.SlugField(blank=True, editable=False)
match = models.CharField(max_length=256, blank=True)
matching_algorithm = models.PositiveIntegerField(
@@ -70,87 +65,9 @@ class MatchingModel(models.Model):
def __str__(self):
return self.name
- @property
- def conditions(self):
- return "{}: \"{}\" ({})".format(
- self.name, self.match, self.get_matching_algorithm_display())
-
- @classmethod
- def match_all(cls, text, tags=None):
-
- if tags is None:
- tags = cls.objects.all()
-
- text = text.lower()
- for tag in tags:
- if tag.matches(text):
- yield tag
-
- def matches(self, text):
-
- search_kwargs = {}
-
- # Check that match is not empty
- if self.match.strip() == "":
- return False
-
- if self.is_insensitive:
- search_kwargs = {"flags": re.IGNORECASE}
-
- if self.matching_algorithm == self.MATCH_ALL:
- for word in self._split_match():
- search_result = re.search(
- r"\b{}\b".format(word), text, **search_kwargs)
- if not search_result:
- return False
- return True
-
- if self.matching_algorithm == self.MATCH_ANY:
- for word in self._split_match():
- if re.search(r"\b{}\b".format(word), text, **search_kwargs):
- return True
- return False
-
- if self.matching_algorithm == self.MATCH_LITERAL:
- return bool(re.search(
- r"\b{}\b".format(self.match), text, **search_kwargs))
-
- if self.matching_algorithm == self.MATCH_REGEX:
- return bool(re.search(
- re.compile(self.match, **search_kwargs), text))
-
- if self.matching_algorithm == self.MATCH_FUZZY:
- match = re.sub(r'[^\w\s]', '', self.match)
- text = re.sub(r'[^\w\s]', '', text)
- if self.is_insensitive:
- match = match.lower()
- text = text.lower()
-
- return True if fuzz.partial_ratio(match, text) >= 90 else False
-
- raise NotImplementedError("Unsupported matching algorithm")
-
- def _split_match(self):
- """
- Splits the match to individual keywords, getting rid of unnecessary
- spaces and grouping quoted words together.
-
- Example:
- ' some random words "with quotes " and spaces'
- ==>
- ["some", "random", "words", "with+quotes", "and", "spaces"]
- """
- findterms = re.compile(r'"([^"]+)"|(\S+)').findall
- normspace = re.compile(r"\s+").sub
- return [
- normspace(" ", (t[0] or t[1]).strip()).replace(" ", r"\s+")
- for t in findterms(self.match)
- ]
-
def save(self, *args, **kwargs):
self.match = self.match.lower()
- self.slug = slugify(self.name)
models.Model.save(self, *args, **kwargs)
@@ -185,20 +102,20 @@ class Tag(MatchingModel):
colour = models.PositiveIntegerField(choices=COLOURS, default=1)
+ is_inbox_tag = models.BooleanField(
+ default=False,
+ help_text="Marks this tag as an inbox tag: All newly consumed "
+ "documents will be tagged with inbox tags."
+ )
+
+
+class DocumentType(MatchingModel):
+
+ pass
+
class Document(models.Model):
- TYPE_PDF = "pdf"
- TYPE_PNG = "png"
- TYPE_JPG = "jpg"
- TYPE_GIF = "gif"
- TYPE_TIF = "tiff"
- TYPE_TXT = "txt"
- TYPE_CSV = "csv"
- TYPE_MD = "md"
- TYPES = (TYPE_PDF, TYPE_PNG, TYPE_JPG, TYPE_GIF, TYPE_TIF,
- TYPE_TXT, TYPE_CSV, TYPE_MD)
-
STORAGE_TYPE_UNENCRYPTED = "unencrypted"
STORAGE_TYPE_GPG = "gpg"
STORAGE_TYPES = (
@@ -216,17 +133,23 @@ class Document(models.Model):
title = models.CharField(max_length=128, blank=True, db_index=True)
- content = models.TextField(
- db_index=True,
+ document_type = models.ForeignKey(
+ DocumentType,
blank=True,
- help_text="The raw, text-only data of the document. This field is "
+ null=True,
+ related_name="documents",
+ on_delete=models.SET_NULL
+ )
+
+ content = models.TextField(
+ blank=True,
+ help_text="The raw, text-only data of the document. This field is "
"primarily used for searching."
)
- file_type = models.CharField(
- max_length=4,
- editable=False,
- choices=tuple([(t, t.upper()) for t in TYPES])
+ mime_type = models.CharField(
+ max_length=256,
+ editable=False
)
tags = models.ManyToManyField(
@@ -236,13 +159,20 @@ class Document(models.Model):
max_length=32,
editable=False,
unique=True,
- help_text="The checksum of the original document (before it was "
- "encrypted). We use this to prevent duplicate document "
- "imports."
+ help_text="The checksum of the original document."
+ )
+
+ archive_checksum = models.CharField(
+ max_length=32,
+ editable=False,
+ blank=True,
+ null=True,
+ help_text="The checksum of the archived document."
)
created = models.DateTimeField(
default=timezone.now, db_index=True)
+
modified = models.DateTimeField(
auto_now=True, editable=False, db_index=True)
@@ -257,144 +187,44 @@ class Document(models.Model):
default=timezone.now, editable=False, db_index=True)
filename = models.FilePathField(
- max_length=256,
+ max_length=1024,
editable=False,
default=None,
null=True,
help_text="Current filename in storage"
)
+ archive_serial_number = models.IntegerField(
+ blank=True,
+ null=True,
+ unique=True,
+ db_index=True,
+ help_text="The position of this document in your physical document "
+ "archive."
+ )
+
class Meta:
- ordering = ("correspondent", "title")
+ ordering = ("-created",)
def __str__(self):
- created = self.created.strftime("%Y%m%d%H%M%S")
+ created = datetime.date.isoformat(self.created)
if self.correspondent and self.title:
- return "{}: {} - {}".format(
- created, self.correspondent, self.title)
- if self.correspondent or self.title:
- return "{}: {}".format(created, self.correspondent or self.title)
- return str(created)
-
- def find_renamed_document(self, subdirectory=""):
- suffix = "%07i.%s" % (self.pk, self.file_type)
-
- # Append .gpg for encrypted files
- if self.storage_type == self.STORAGE_TYPE_GPG:
- suffix += ".gpg"
-
- # Go up in the directory hierarchy and try to delete all directories
- root = os.path.normpath(Document.filename_to_path(subdirectory))
-
- for filename in os.listdir(root):
- if filename.endswith(suffix):
- return os.path.join(subdirectory, filename)
-
- fullname = os.path.join(subdirectory, filename)
- if os.path.isdir(Document.filename_to_path(fullname)):
- return self.find_renamed_document(fullname)
-
- return None
-
- @property
- def source_filename(self):
- # Initial filename generation (for new documents)
- if self.filename is None:
- self.filename = self.generate_source_filename()
-
- # Check if document is still available under filename
- elif not os.path.isfile(Document.filename_to_path(self.filename)):
- recovered_filename = self.find_renamed_document()
-
- # If we have found the file so update the filename
- if recovered_filename is not None:
- logger = logging.getLogger(__name__)
- logger.warning("Filename of document " + str(self.id) +
- " has changed and was successfully updated")
- self.filename = recovered_filename
-
- # Remove all empty subdirectories from MEDIA_ROOT
- Document.delete_all_empty_subdirectories(
- Document.filename_to_path(""))
- else:
- logger = logging.getLogger(__name__)
- logger.error("File of document " + str(self.id) + " has " +
- "gone and could not be recovered")
-
- return self.filename
-
- @staticmethod
- def many_to_dictionary(field):
- # Converts ManyToManyField to dictionary by assuming, that field
- # entries contain an _ or - which will be used as a delimiter
- mydictionary = dict()
-
- for index, t in enumerate(field.all()):
- # Populate tag names by index
- mydictionary[index] = slugify(t.name)
-
- # Find delimiter
- delimiter = t.name.find('_')
-
- if delimiter == -1:
- delimiter = t.name.find('-')
-
- if delimiter == -1:
- continue
-
- key = t.name[:delimiter]
- value = t.name[delimiter+1:]
-
- mydictionary[slugify(key)] = slugify(value)
-
- return mydictionary
-
- def generate_source_filename(self):
- # Create filename based on configured format
- if settings.PAPERLESS_FILENAME_FORMAT is not None:
- tags = defaultdict(lambda: slugify(None),
- self.many_to_dictionary(self.tags))
- path = settings.PAPERLESS_FILENAME_FORMAT.format(
- correspondent=slugify(self.correspondent),
- title=slugify(self.title),
- created=slugify(self.created),
- added=slugify(self.added),
- tags=tags)
+ return f"{created} {self.correspondent} {self.title}"
else:
- path = ""
-
- # Always append the primary key to guarantee uniqueness of filename
- if len(path) > 0:
- filename = "%s-%07i.%s" % (path, self.pk, self.file_type)
- else:
- filename = "%07i.%s" % (self.pk, self.file_type)
-
- # Append .gpg for encrypted files
- if self.storage_type == self.STORAGE_TYPE_GPG:
- filename += ".gpg"
-
- return filename
-
- def create_source_directory(self):
- new_filename = self.generate_source_filename()
-
- # Determine the full "target" path
- dir_new = Document.filename_to_path(os.path.dirname(new_filename))
-
- # Create new path
- os.makedirs(dir_new, exist_ok=True)
+ return f"{created} {self.title}"
@property
def source_path(self):
- return Document.filename_to_path(self.source_filename)
+ if self.filename:
+ fname = str(self.filename)
+ else:
+ fname = "{:07}{}".format(self.pk, self.file_type)
+ if self.storage_type == self.STORAGE_TYPE_GPG:
+ fname += ".gpg" # pragma: no cover
- @staticmethod
- def filename_to_path(filename):
return os.path.join(
- settings.MEDIA_ROOT,
- "documents",
- "originals",
- filename
+ settings.ORIGINALS_DIR,
+ fname
)
@property
@@ -402,24 +232,49 @@ class Document(models.Model):
return open(self.source_path, "rb")
@property
- def file_name(self):
- return slugify(str(self)) + "." + self.file_type
+ def archive_path(self):
+ if self.filename:
+ fname = archive_name_from_filename(self.filename)
+ else:
+ fname = "{:07}.pdf".format(self.pk)
+
+ return os.path.join(
+ settings.ARCHIVE_DIR,
+ fname
+ )
@property
- def download_url(self):
- return reverse("fetch", kwargs={"kind": "doc", "pk": self.pk})
+ def archive_file(self):
+ return open(self.archive_path, "rb")
+
+ def get_public_filename(self, archive=False, counter=0, suffix=None):
+ result = str(self)
+
+ if counter:
+ result += f"_{counter:02}"
+
+ if suffix:
+ result += suffix
+
+ if archive:
+ result += ".pdf"
+ else:
+ result += self.file_type
+
+ return pathvalidate.sanitize_filename(result, replacement_text="-")
+
+ @property
+ def file_type(self):
+ return get_default_file_extension(self.mime_type)
@property
def thumbnail_path(self):
-
file_name = "{:07}.png".format(self.pk)
if self.storage_type == self.STORAGE_TYPE_GPG:
file_name += ".gpg"
return os.path.join(
- settings.MEDIA_ROOT,
- "documents",
- "thumbnails",
+ settings.THUMBNAIL_DIR,
file_name
)
@@ -427,129 +282,6 @@ class Document(models.Model):
def thumbnail_file(self):
return open(self.thumbnail_path, "rb")
- @property
- def thumbnail_url(self):
- return reverse("fetch", kwargs={"kind": "thumb", "pk": self.pk})
-
- def set_filename(self, filename):
- if os.path.isfile(Document.filename_to_path(filename)):
- self.filename = filename
-
- @staticmethod
- def try_delete_empty_directories(directory):
- # Go up in the directory hierarchy and try to delete all directories
- directory = os.path.normpath(directory)
- root = os.path.normpath(Document.filename_to_path(""))
-
- while directory != root:
- # Try to delete the current directory
- try:
- os.rmdir(directory)
- except os.error:
- # Directory not empty, no need to go further up
- return
-
- # Cut off actual directory and go one level up
- directory, _ = os.path.split(directory)
- directory = os.path.normpath(directory)
-
- @staticmethod
- def delete_all_empty_subdirectories(directory):
- # Go through all folders and try to delete all directories
- root = os.path.normpath(Document.filename_to_path(directory))
-
- for filename in os.listdir(root):
- fullname = os.path.join(directory, filename)
-
- if not os.path.isdir(Document.filename_to_path(fullname)):
- continue
-
- # Go into subdirectory to see, if there is more to delete
- Document.delete_all_empty_subdirectories(
- os.path.join(directory, filename))
-
- # Try to delete the directory
- try:
- os.rmdir(Document.filename_to_path(fullname))
- continue
- except os.error:
- # Directory not empty, no need to go further up
- continue
-
-
-@receiver(models.signals.m2m_changed, sender=Document.tags.through)
-@receiver(models.signals.post_save, sender=Document)
-def update_filename(sender, instance, **kwargs):
- # Skip if document has not been saved yet
- if instance.filename is None:
- return
-
- # Check is file exists and update filename otherwise
- if not os.path.isfile(Document.filename_to_path(instance.filename)):
- instance.filename = instance.source_filename
-
- # Build the new filename
- new_filename = instance.generate_source_filename()
-
- # If the filename is the same, then nothing needs to be done
- if instance.filename == new_filename:
- return
-
- # Determine the full "target" path
- path_new = instance.filename_to_path(new_filename)
- dir_new = instance.filename_to_path(os.path.dirname(new_filename))
-
- # Create new path
- instance.create_source_directory()
-
- # Determine the full "current" path
- path_current = instance.filename_to_path(instance.source_filename)
-
- # Move file
- try:
- os.rename(path_current, path_new)
- except PermissionError:
- # Do not update filename in object
- return
- except FileNotFoundError:
- logger = logging.getLogger(__name__)
- logger.error("Renaming of document " + str(instance.id) + " failed " +
- "as file " + instance.filename + " was no longer present")
- return
-
- # Delete empty directory
- old_dir = os.path.dirname(instance.filename)
- old_path = instance.filename_to_path(old_dir)
- Document.try_delete_empty_directories(old_path)
-
- instance.filename = new_filename
-
- # Save instance
- # This will not cause a cascade of post_save signals, as next time
- # nothing needs to be renamed
- instance.save()
-
-
-@receiver(models.signals.post_delete, sender=Document)
-def delete_files(sender, instance, **kwargs):
- if instance.filename is None:
- return
-
- # Remove the document
- old_file = instance.filename_to_path(instance.filename)
-
- try:
- os.remove(old_file)
- except FileNotFoundError:
- logger = logging.getLogger(__name__)
- logger.warning("Deleted document " + str(instance.id) + " but file " +
- old_file + " was no longer present")
-
- # And remove the directory (if applicable)
- old_dir = os.path.dirname(instance.filename)
- old_path = instance.filename_to_path(old_dir)
- Document.try_delete_empty_directories(old_path)
-
class Log(models.Model):
@@ -561,34 +293,68 @@ class Log(models.Model):
(logging.CRITICAL, "Critical"),
)
- group = models.UUIDField(blank=True)
+ group = models.UUIDField(blank=True, null=True)
message = models.TextField()
level = models.PositiveIntegerField(choices=LEVELS, default=logging.INFO)
created = models.DateTimeField(auto_now_add=True)
- modified = models.DateTimeField(auto_now=True)
-
- objects = LogManager()
class Meta:
- ordering = ("-modified",)
+ ordering = ("-created",)
def __str__(self):
return self.message
- def save(self, *args, **kwargs):
- """
- To allow for the case where we don't want to group the message, we
- shouldn't force the caller to specify a one-time group value. However,
- allowing group=None means that the manager can't differentiate the
- different un-grouped messages, so instead we set a random one here.
- """
- if not self.group:
- self.group = uuid.uuid4()
+class SavedView(models.Model):
- models.Model.save(self, *args, **kwargs)
+ class Meta:
+
+ ordering = ("name",)
+
+ user = models.ForeignKey(User, on_delete=models.CASCADE)
+ name = models.CharField(max_length=128)
+
+ show_on_dashboard = models.BooleanField()
+ show_in_sidebar = models.BooleanField()
+
+ sort_field = models.CharField(max_length=128)
+ sort_reverse = models.BooleanField(default=False)
+class SavedViewFilterRule(models.Model):
+ RULE_TYPES = [
+ (0, "Title contains"),
+ (1, "Content contains"),
+ (2, "ASN is"),
+ (3, "Correspondent is"),
+ (4, "Document type is"),
+ (5, "Is in inbox"),
+ (6, "Has tag"),
+ (7, "Has any tag"),
+ (8, "Created before"),
+ (9, "Created after"),
+ (10, "Created year is"),
+ (11, "Created month is"),
+ (12, "Created day is"),
+ (13, "Added before"),
+ (14, "Added after"),
+ (15, "Modified before"),
+ (16, "Modified after"),
+ (17, "Does not have tag"),
+ ]
+
+ saved_view = models.ForeignKey(
+ SavedView,
+ on_delete=models.CASCADE,
+ related_name="filter_rules"
+ )
+
+ rule_type = models.PositiveIntegerField(choices=RULE_TYPES)
+
+ value = models.CharField(max_length=128)
+
+
+# TODO: why is this in the models file?
class FileInfo:
# This epic regex *almost* worked for our needs, so I'm keeping it here for
@@ -603,53 +369,44 @@ class FileInfo:
non_separated_word=r"([\w,. ]|([^\s]-))"
)
)
-
- formats = "pdf|jpe?g|png|gif|tiff?|te?xt|md|csv"
REGEXES = OrderedDict([
("created-correspondent-title-tags", re.compile(
r"^(?P\d\d\d\d\d\d\d\d(\d\d\d\d\d\d)?Z) - "
r"(?P.*) - "
r"(?P.*) - "
- r"(?P[a-z0-9\-,]*)"
- r"\.(?P{})$".format(formats),
+ r"(?P[a-z0-9\-,]*)$",
flags=re.IGNORECASE
)),
("created-title-tags", re.compile(
r"^(?P\d\d\d\d\d\d\d\d(\d\d\d\d\d\d)?Z) - "
r"(?P.*) - "
- r"(?P[a-z0-9\-,]*)"
- r"\.(?P{})$".format(formats),
+ r"(?P[a-z0-9\-,]*)$",
flags=re.IGNORECASE
)),
("created-correspondent-title", re.compile(
r"^(?P\d\d\d\d\d\d\d\d(\d\d\d\d\d\d)?Z) - "
r"(?P.*) - "
- r"(?P.*)"
- r"\.(?P{})$".format(formats),
+ r"(?P.*)$",
flags=re.IGNORECASE
)),
("created-title", re.compile(
r"^(?P\d\d\d\d\d\d\d\d(\d\d\d\d\d\d)?Z) - "
- r"(?P.*)"
- r"\.(?P{})$".format(formats),
+ r"(?P.*)$",
flags=re.IGNORECASE
)),
("correspondent-title-tags", re.compile(
r"(?P.*) - "
r"(?P.*) - "
- r"(?P[a-z0-9\-,]*)"
- r"\.(?P{})$".format(formats),
+ r"(?P[a-z0-9\-,]*)$",
flags=re.IGNORECASE
)),
("correspondent-title", re.compile(
r"(?P.*) - "
- r"(?P.*)?"
- r"\.(?P{})$".format(formats),
+ r"(?P.*)?$",
flags=re.IGNORECASE
)),
("title", re.compile(
- r"(?P.*)"
- r"\.(?P{})$".format(formats),
+ r"(?P.*)$",
flags=re.IGNORECASE
))
])
@@ -674,9 +431,7 @@ class FileInfo:
def _get_correspondent(cls, name):
if not name:
return None
- return Correspondent.objects.get_or_create(name=name, defaults={
- "slug": slugify(name)
- })[0]
+ return Correspondent.objects.get_or_create(name=name)[0]
@classmethod
def _get_title(cls, title):
@@ -686,21 +441,9 @@ class FileInfo:
def _get_tags(cls, tags):
r = []
for t in tags.split(","):
- r.append(Tag.objects.get_or_create(
- slug=slugify(t),
- defaults={"name": t}
- )[0])
+ r.append(Tag.objects.get_or_create(name=t)[0])
return tuple(r)
- @classmethod
- def _get_extension(cls, extension):
- r = extension.lower()
- if r == "jpeg":
- return "jpg"
- if r == "tif":
- return "tiff"
- return r
-
@classmethod
def _mangle_property(cls, properties, name):
if name in properties:
@@ -709,18 +452,16 @@ class FileInfo:
)
@classmethod
- def from_path(cls, path):
+ def from_filename(cls, filename):
"""
We use a crude naming convention to make handling the correspondent,
title, and tags easier:
- " - - - ."
- " - - ."
- " - ."
- "."
+ " - - - "
+ " - - "
+ " - "
+ ""
"""
- filename = os.path.basename(path)
-
# Mutate filename in-place before parsing its components
# by applying at most one of the configured transformations.
for (pattern, repl) in settings.FILENAME_PARSE_TRANSFORMS:
@@ -728,6 +469,23 @@ class FileInfo:
if count:
break
+ # do this after the transforms so that the transforms can do whatever
+ # with the file extension.
+ filename_no_ext = os.path.splitext(filename)[0]
+
+ if filename_no_ext == filename and filename.startswith("."):
+ # This is a very special case where there is no text before the
+ # file type.
+ # TODO: this should be handled better. The ext is not removed
+ # because usually, files like '.pdf' are just hidden files
+ # with the name pdf, but in our case, its more likely that
+ # there's just no name to begin with.
+ filename = ""
+ # This isn't too bad either, since we'll just not match anything
+ # and return an empty title. TODO: actually, this is kinda bad.
+ else:
+ filename = filename_no_ext
+
# Parse filename components.
for regex in cls.REGEXES.values():
m = regex.match(filename)
@@ -737,5 +495,4 @@ class FileInfo:
cls._mangle_property(properties, "correspondent")
cls._mangle_property(properties, "title")
cls._mangle_property(properties, "tags")
- cls._mangle_property(properties, "extension")
return cls(**properties)
diff --git a/src/documents/parsers.py b/src/documents/parsers.py
index c0a80a55d..cbbb912de 100644
--- a/src/documents/parsers.py
+++ b/src/documents/parsers.py
@@ -1,4 +1,5 @@
import logging
+import mimetypes
import os
import re
import shutil
@@ -6,6 +7,7 @@ import subprocess
import tempfile
import dateparser
+import magic
from django.conf import settings
from django.utils import timezone
@@ -20,146 +22,237 @@ from django.utils import timezone
# - XX. MONTH ZZZZ with XX being 1 or 2 and ZZZZ being 2 or 4 digits
# - MONTH ZZZZ, with ZZZZ being 4 digits
# - MONTH XX, ZZZZ with XX being 1 or 2 and ZZZZ being 4 digits
+from documents.loggers import LoggingMixin
+from documents.signals import document_consumer_declaration
+
+# TODO: isnt there a date parsing library for this?
+
DATE_REGEX = re.compile(
- r'(\b|(?!=([_-])))([0-9]{1,2})[\.\/-]([0-9]{1,2})[\.\/-]([0-9]{4}|[0-9]{2})(\b|(?=([_-])))|' + # NOQA: E501
- r'(\b|(?!=([_-])))([0-9]{4}|[0-9]{2})[\.\/-]([0-9]{1,2})[\.\/-]([0-9]{1,2})(\b|(?=([_-])))|' + # NOQA: E501
- r'(\b|(?!=([_-])))([0-9]{1,2}[\. ]+[^ ]{3,9} ([0-9]{4}|[0-9]{2}))(\b|(?=([_-])))|' + # NOQA: E501
- r'(\b|(?!=([_-])))([^\W\d_]{3,9} [0-9]{1,2}, ([0-9]{4}))(\b|(?=([_-])))|' +
+ r'(\b|(?!=([_-])))([0-9]{1,2})[\.\/-]([0-9]{1,2})[\.\/-]([0-9]{4}|[0-9]{2})(\b|(?=([_-])))|' # NOQA: E501
+ r'(\b|(?!=([_-])))([0-9]{4}|[0-9]{2})[\.\/-]([0-9]{1,2})[\.\/-]([0-9]{1,2})(\b|(?=([_-])))|' # NOQA: E501
+ r'(\b|(?!=([_-])))([0-9]{1,2}[\. ]+[^ ]{3,9} ([0-9]{4}|[0-9]{2}))(\b|(?=([_-])))|' # NOQA: E501
+ r'(\b|(?!=([_-])))([^\W\d_]{3,9} [0-9]{1,2}, ([0-9]{4}))(\b|(?=([_-])))|'
r'(\b|(?!=([_-])))([^\W\d_]{3,9} [0-9]{4})(\b|(?=([_-])))'
)
+logger = logging.getLogger(__name__)
+
+
+def is_mime_type_supported(mime_type):
+ return get_parser_class_for_mime_type(mime_type) is not None
+
+
+def get_default_file_extension(mime_type):
+ for response in document_consumer_declaration.send(None):
+ parser_declaration = response[1]
+ supported_mime_types = parser_declaration["mime_types"]
+
+ if mime_type in supported_mime_types:
+ return supported_mime_types[mime_type]
+
+ ext = mimetypes.guess_extension(mime_type)
+ if ext:
+ return ext
+ else:
+ return ""
+
+
+def is_file_ext_supported(ext):
+ if ext:
+ return ext.lower() in get_supported_file_extensions()
+ else:
+ return False
+
+
+def get_supported_file_extensions():
+ extensions = set()
+ for response in document_consumer_declaration.send(None):
+ parser_declaration = response[1]
+ supported_mime_types = parser_declaration["mime_types"]
+
+ for mime_type in supported_mime_types:
+ extensions.update(mimetypes.guess_all_extensions(mime_type))
+
+ return extensions
+
+
+def get_parser_class_for_mime_type(mime_type):
+
+ options = []
+
+ # Sein letzter Befehl war: KOMMT! Und sie kamen. Alle. Sogar die Parser.
+
+ for response in document_consumer_declaration.send(None):
+ parser_declaration = response[1]
+ supported_mime_types = parser_declaration["mime_types"]
+
+ if mime_type in supported_mime_types:
+ options.append(parser_declaration)
+
+ if not options:
+ return None
+
+ # Return the parser with the highest weight.
+ return sorted(
+ options, key=lambda _: _["weight"], reverse=True)[0]["parser"]
+
+
+def get_parser_class(path):
+ """
+ Determine the appropriate parser class based on the file
+ """
+
+ mime_type = magic.from_file(path, mime=True)
+
+ return get_parser_class_for_mime_type(mime_type)
+
+
+def run_convert(input_file,
+ output_file,
+ density=None,
+ scale=None,
+ alpha=None,
+ strip=False,
+ trim=False,
+ type=None,
+ depth=None,
+ extra=None,
+ logging_group=None):
+
+ environment = os.environ.copy()
+ if settings.CONVERT_MEMORY_LIMIT:
+ environment["MAGICK_MEMORY_LIMIT"] = settings.CONVERT_MEMORY_LIMIT
+ if settings.CONVERT_TMPDIR:
+ environment["MAGICK_TMPDIR"] = settings.CONVERT_TMPDIR
+
+ args = [settings.CONVERT_BINARY]
+ args += ['-density', str(density)] if density else []
+ args += ['-scale', str(scale)] if scale else []
+ args += ['-alpha', str(alpha)] if alpha else []
+ args += ['-strip'] if strip else []
+ args += ['-trim'] if trim else []
+ args += ['-type', str(type)] if type else []
+ args += ['-depth', str(depth)] if depth else []
+ args += [input_file, output_file]
+
+ logger.debug("Execute: " + " ".join(args), extra={'group': logging_group})
+
+ if not subprocess.Popen(args, env=environment).wait() == 0:
+ raise ParseError("Convert failed at {}".format(args))
+
+
+def parse_date(filename, text):
+ """
+ Returns the date of the document.
+ """
+
+ def __parser(ds, date_order):
+ """
+ Call dateparser.parse with a particular date ordering
+ """
+ return dateparser.parse(
+ ds,
+ settings={
+ "DATE_ORDER": date_order,
+ "PREFER_DAY_OF_MONTH": "first",
+ "RETURN_AS_TIMEZONE_AWARE":
+ True
+ }
+ )
+
+ date = None
+
+ # if filename date parsing is enabled, search there first:
+ if settings.FILENAME_DATE_ORDER:
+ for m in re.finditer(DATE_REGEX, filename):
+ date_string = m.group(0)
+
+ try:
+ date = __parser(date_string, settings.FILENAME_DATE_ORDER)
+ except (TypeError, ValueError):
+ # Skip all matches that do not parse to a proper date
+ continue
+
+ if date and date.year > 1900 and date <= timezone.now():
+ return date
+
+ # Iterate through all regex matches in text and try to parse the date
+ for m in re.finditer(DATE_REGEX, text):
+ date_string = m.group(0)
+
+ try:
+ date = __parser(date_string, settings.DATE_ORDER)
+ except (TypeError, ValueError):
+ # Skip all matches that do not parse to a proper date
+ continue
+
+ if date and date.year > 1900 and date <= timezone.now():
+ break
+ else:
+ date = None
+
+ return date
+
+
class ParseError(Exception):
pass
-class DocumentParser:
+class DocumentParser(LoggingMixin):
"""
Subclass this to make your own parser. Have a look at
`paperless_tesseract.parsers` for inspiration.
"""
- SCRATCH = settings.SCRATCH_DIR
- DATE_ORDER = settings.DATE_ORDER
- FILENAME_DATE_ORDER = settings.FILENAME_DATE_ORDER
- OPTIPNG = settings.OPTIPNG_BINARY
+ def __init__(self, logging_group):
+ super().__init__()
+ self.logging_group = logging_group
+ os.makedirs(settings.SCRATCH_DIR, exist_ok=True)
+ self.tempdir = tempfile.mkdtemp(
+ prefix="paperless-", dir=settings.SCRATCH_DIR)
- def __init__(self, path):
- self.document_path = path
- self.tempdir = tempfile.mkdtemp(prefix="paperless-", dir=self.SCRATCH)
- self.logger = logging.getLogger(__name__)
- self.logging_group = None
+ self.archive_path = None
+ self.text = None
+ self.date = None
- def get_thumbnail(self):
+ def extract_metadata(self, document_path, mime_type):
+ return []
+
+ def parse(self, document_path, mime_type):
+ raise NotImplementedError()
+
+ def get_archive_path(self):
+ return self.archive_path
+
+ def get_thumbnail(self, document_path, mime_type):
"""
Returns the path to a file we can use as a thumbnail for this document.
"""
raise NotImplementedError()
- def optimise_thumbnail(self, in_path):
+ def get_optimised_thumbnail(self, document_path, mime_type):
+ thumbnail = self.get_thumbnail(document_path, mime_type)
+ if settings.OPTIMIZE_THUMBNAILS:
+ out_path = os.path.join(self.tempdir, "thumb_optipng.png")
- out_path = os.path.join(self.tempdir, "optipng.png")
+ args = (settings.OPTIPNG_BINARY,
+ "-silent", "-o5", thumbnail, "-out", out_path)
- args = (self.OPTIPNG, "-o5", in_path, "-out", out_path)
- if not subprocess.Popen(args).wait() == 0:
- raise ParseError("Optipng failed at {}".format(args))
+ self.log('debug', f"Execute: {' '.join(args)}")
- return out_path
+ if not subprocess.Popen(args).wait() == 0:
+ raise ParseError("Optipng failed at {}".format(args))
- def get_optimised_thumbnail(self):
- return self.optimise_thumbnail(self.get_thumbnail())
+ return out_path
+ else:
+ return thumbnail
def get_text(self):
- """
- Returns the text from the document and only the text.
- """
- raise NotImplementedError()
+ return self.text
def get_date(self):
- """
- Returns the date of the document.
- """
-
- def __parser(ds, date_order):
- """
- Call dateparser.parse with a particular date ordering
- """
- return dateparser.parse(
- ds,
- settings={
- "DATE_ORDER": date_order,
- "PREFER_DAY_OF_MONTH": "first",
- "RETURN_AS_TIMEZONE_AWARE":
- True
- }
- )
-
- date = None
- date_string = None
-
- next_year = timezone.now().year + 5 # Arbitrary 5 year future limit
- title = os.path.basename(self.document_path)
-
- # if filename date parsing is enabled, search there first:
- if self.FILENAME_DATE_ORDER:
- self.log("info", "Checking document title for date")
- for m in re.finditer(DATE_REGEX, title):
- date_string = m.group(0)
-
- try:
- date = __parser(date_string, self.FILENAME_DATE_ORDER)
- except (TypeError, ValueError):
- # Skip all matches that do not parse to a proper date
- continue
-
- if date is not None and next_year > date.year > 1900:
- self.log(
- "info",
- "Detected document date {} based on string {} "
- "from document title"
- "".format(date.isoformat(), date_string)
- )
- return date
-
- try:
- # getting text after checking filename will save time if only
- # looking at the filename instead of the whole text
- text = self.get_text()
- except ParseError:
- return None
-
- # Iterate through all regex matches in text and try to parse the date
- for m in re.finditer(DATE_REGEX, text):
- date_string = m.group(0)
-
- try:
- date = __parser(date_string, self.DATE_ORDER)
- except (TypeError, ValueError):
- # Skip all matches that do not parse to a proper date
- continue
-
- if date is not None and next_year > date.year > 1900:
- break
- else:
- date = None
-
- if date is not None:
- self.log(
- "info",
- "Detected document date {} based on string {}".format(
- date.isoformat(),
- date_string
- )
- )
- else:
- self.log("info", "Unable to detect date for document")
-
- return date
-
- def log(self, level, message):
- getattr(self.logger, level)(message, extra={
- "group": self.logging_group
- })
+ return self.date
def cleanup(self):
self.log("debug", "Deleting directory {}".format(self.tempdir))
diff --git a/src/documents/sanity_checker.py b/src/documents/sanity_checker.py
new file mode 100644
index 000000000..bc0b689d4
--- /dev/null
+++ b/src/documents/sanity_checker.py
@@ -0,0 +1,121 @@
+import hashlib
+import os
+
+from django.conf import settings
+
+from documents.models import Document
+
+
+class SanityMessage:
+ message = None
+
+
+class SanityWarning(SanityMessage):
+ def __init__(self, message):
+ self.message = message
+
+ def __str__(self):
+ return f"Warning: {self.message}"
+
+
+class SanityError(SanityMessage):
+ def __init__(self, message):
+ self.message = message
+
+ def __str__(self):
+ return f"ERROR: {self.message}"
+
+
+class SanityFailedError(Exception):
+
+ def __init__(self, messages):
+ self.messages = messages
+
+ def __str__(self):
+ message_string = "\n".join([str(m) for m in self.messages])
+ return (
+ f"The following issuse were found by the sanity checker:\n"
+ f"{message_string}\n\n===============\n\n")
+
+
+def check_sanity():
+ messages = []
+
+ present_files = []
+ for root, subdirs, files in os.walk(settings.MEDIA_ROOT):
+ for f in files:
+ present_files.append(os.path.normpath(os.path.join(root, f)))
+
+ lockfile = os.path.normpath(settings.MEDIA_LOCK)
+ if lockfile in present_files:
+ present_files.remove(lockfile)
+
+ for doc in Document.objects.all():
+ # Check sanity of the thumbnail
+ if not os.path.isfile(doc.thumbnail_path):
+ messages.append(SanityError(
+ f"Thumbnail of document {doc.pk} does not exist."))
+ else:
+ present_files.remove(os.path.normpath(doc.thumbnail_path))
+ try:
+ with doc.thumbnail_file as f:
+ f.read()
+ except OSError as e:
+ messages.append(SanityError(
+ f"Cannot read thumbnail file of document {doc.pk}: {e}"
+ ))
+
+ # Check sanity of the original file
+ # TODO: extract method
+ if not os.path.isfile(doc.source_path):
+ messages.append(SanityError(
+ f"Original of document {doc.pk} does not exist."))
+ else:
+ present_files.remove(os.path.normpath(doc.source_path))
+ try:
+ with doc.source_file as f:
+ checksum = hashlib.md5(f.read()).hexdigest()
+ except OSError as e:
+ messages.append(SanityError(
+ f"Cannot read original file of document {doc.pk}: {e}"))
+ else:
+ if not checksum == doc.checksum:
+ messages.append(SanityError(
+ f"Checksum mismatch of document {doc.pk}. "
+ f"Stored: {doc.checksum}, actual: {checksum}."
+ ))
+
+ # Check sanity of the archive file.
+ if doc.archive_checksum:
+ if not os.path.isfile(doc.archive_path):
+ messages.append(SanityError(
+ f"Archived version of document {doc.pk} does not exist."
+ ))
+ else:
+ present_files.remove(os.path.normpath(doc.archive_path))
+ try:
+ with doc.archive_file as f:
+ checksum = hashlib.md5(f.read()).hexdigest()
+ except OSError as e:
+ messages.append(SanityError(
+ f"Cannot read archive file of document {doc.pk}: {e}"
+ ))
+ else:
+ if not checksum == doc.archive_checksum:
+ messages.append(SanityError(
+ f"Checksum mismatch of archive {doc.pk}. "
+ f"Stored: {doc.checksum}, actual: {checksum}."
+ ))
+
+ # other document checks
+ if not doc.content:
+ messages.append(SanityWarning(
+ f"Document {doc.pk} has no content."
+ ))
+
+ for extra_file in present_files:
+ messages.append(SanityWarning(
+ f"Orphaned file in media dir: {extra_file}"
+ ))
+
+ return messages
diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py
index 0075d4de7..ee0a42384 100644
--- a/src/documents/serialisers.py
+++ b/src/documents/serialisers.py
@@ -1,9 +1,22 @@
+import magic
+from django.utils.text import slugify
from rest_framework import serializers
+from rest_framework.fields import SerializerMethodField
-from .models import Correspondent, Tag, Document, Log
+from .models import Correspondent, Tag, Document, Log, DocumentType, \
+ SavedView, SavedViewFilterRule
+from .parsers import is_mime_type_supported
-class CorrespondentSerializer(serializers.HyperlinkedModelSerializer):
+class CorrespondentSerializer(serializers.ModelSerializer):
+
+ document_count = serializers.IntegerField(read_only=True)
+
+ last_correspondence = serializers.DateTimeField(read_only=True)
+
+ def get_slug(self, obj):
+ return slugify(obj.name)
+ slug = SerializerMethodField()
class Meta:
model = Correspondent
@@ -13,11 +26,40 @@ class CorrespondentSerializer(serializers.HyperlinkedModelSerializer):
"name",
"match",
"matching_algorithm",
- "is_insensitive"
+ "is_insensitive",
+ "document_count",
+ "last_correspondence"
)
-class TagSerializer(serializers.HyperlinkedModelSerializer):
+class DocumentTypeSerializer(serializers.ModelSerializer):
+
+ document_count = serializers.IntegerField(read_only=True)
+
+ def get_slug(self, obj):
+ return slugify(obj.name)
+ slug = SerializerMethodField()
+
+ class Meta:
+ model = DocumentType
+ fields = (
+ "id",
+ "slug",
+ "name",
+ "match",
+ "matching_algorithm",
+ "is_insensitive",
+ "document_count"
+ )
+
+
+class TagSerializer(serializers.ModelSerializer):
+
+ document_count = serializers.IntegerField(read_only=True)
+
+ def get_slug(self, obj):
+ return slugify(obj.name)
+ slug = SerializerMethodField()
class Meta:
model = Tag
@@ -28,53 +70,184 @@ class TagSerializer(serializers.HyperlinkedModelSerializer):
"colour",
"match",
"matching_algorithm",
- "is_insensitive"
+ "is_insensitive",
+ "is_inbox_tag",
+ "document_count"
)
-class CorrespondentField(serializers.HyperlinkedRelatedField):
+class CorrespondentField(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
return Correspondent.objects.all()
-class TagsField(serializers.HyperlinkedRelatedField):
+class TagsField(serializers.PrimaryKeyRelatedField):
def get_queryset(self):
return Tag.objects.all()
+class DocumentTypeField(serializers.PrimaryKeyRelatedField):
+ def get_queryset(self):
+ return DocumentType.objects.all()
+
+
class DocumentSerializer(serializers.ModelSerializer):
- correspondent = CorrespondentField(
- view_name="drf:correspondent-detail", allow_null=True)
- tags = TagsField(view_name="drf:tag-detail", many=True)
+ correspondent = CorrespondentField(allow_null=True)
+ tags = TagsField(many=True)
+ document_type = DocumentTypeField(allow_null=True)
+
+ original_file_name = SerializerMethodField()
+ archived_file_name = SerializerMethodField()
+
+ def get_original_file_name(self, obj):
+ return obj.get_public_filename()
+
+ def get_archived_file_name(self, obj):
+ if obj.archive_checksum:
+ return obj.get_public_filename(archive=True)
+ else:
+ return None
class Meta:
model = Document
+ depth = 1
fields = (
"id",
"correspondent",
+ "document_type",
"title",
"content",
- "file_type",
"tags",
- "checksum",
"created",
"modified",
"added",
- "file_name",
- "download_url",
- "thumbnail_url",
+ "archive_serial_number",
+ "original_file_name",
+ "archived_file_name",
)
class LogSerializer(serializers.ModelSerializer):
- time = serializers.DateTimeField()
- messages = serializers.CharField()
-
class Meta:
model = Log
fields = (
- "time",
- "messages"
+ "id",
+ "created",
+ "message",
+ "group",
+ "level"
)
+
+
+class SavedViewFilterRuleSerializer(serializers.ModelSerializer):
+
+ class Meta:
+ model = SavedViewFilterRule
+ fields = ["rule_type", "value"]
+
+
+class SavedViewSerializer(serializers.ModelSerializer):
+
+ filter_rules = SavedViewFilterRuleSerializer(many=True)
+
+ class Meta:
+ model = SavedView
+ depth = 1
+ fields = ["id", "name", "show_on_dashboard", "show_in_sidebar",
+ "sort_field", "sort_reverse", "filter_rules"]
+
+ def update(self, instance, validated_data):
+ if 'filter_rules' in validated_data:
+ rules_data = validated_data.pop('filter_rules')
+ else:
+ rules_data = None
+ super(SavedViewSerializer, self).update(instance, validated_data)
+ if rules_data is not None:
+ SavedViewFilterRule.objects.filter(saved_view=instance).delete()
+ for rule_data in rules_data:
+ SavedViewFilterRule.objects.create(
+ saved_view=instance, **rule_data)
+ return instance
+
+ def create(self, validated_data):
+ rules_data = validated_data.pop('filter_rules')
+ saved_view = SavedView.objects.create(**validated_data)
+ for rule_data in rules_data:
+ SavedViewFilterRule.objects.create(
+ saved_view=saved_view, **rule_data)
+ return saved_view
+
+
+class PostDocumentSerializer(serializers.Serializer):
+
+ document = serializers.FileField(
+ label="Document",
+ write_only=True,
+ )
+
+ title = serializers.CharField(
+ label="Title",
+ write_only=True,
+ required=False,
+ )
+
+ correspondent = serializers.PrimaryKeyRelatedField(
+ queryset=Correspondent.objects.all(),
+ label="Correspondent",
+ allow_null=True,
+ write_only=True,
+ required=False,
+ )
+
+ document_type = serializers.PrimaryKeyRelatedField(
+ queryset=DocumentType.objects.all(),
+ label="Document type",
+ allow_null=True,
+ write_only=True,
+ required=False,
+ )
+
+ tags = serializers.PrimaryKeyRelatedField(
+ many=True,
+ queryset=Tag.objects.all(),
+ label="Tags",
+ write_only=True,
+ required=False,
+ )
+
+ def validate_document(self, document):
+ document_data = document.file.read()
+ mime_type = magic.from_buffer(document_data, mime=True)
+
+ if not is_mime_type_supported(mime_type):
+ raise serializers.ValidationError(
+ "This file type is not supported.")
+
+ return document.name, document_data
+
+ def validate_title(self, title):
+ if title:
+ return title
+ else:
+ # do not return empty strings.
+ return None
+
+ def validate_correspondent(self, correspondent):
+ if correspondent:
+ return correspondent.id
+ else:
+ return None
+
+ def validate_document_type(self, document_type):
+ if document_type:
+ return document_type.id
+ else:
+ return None
+
+ def validate_tags(self, tags):
+ if tags:
+ return [tag.id for tag in tags]
+ else:
+ return None
diff --git a/src/documents/settings.py b/src/documents/settings.py
index 20b0b2023..c591d397d 100644
--- a/src/documents/settings.py
+++ b/src/documents/settings.py
@@ -2,3 +2,4 @@
# for exporting/importing commands
EXPORTER_FILE_NAME = "__exported_file_name__"
EXPORTER_THUMBNAIL_NAME = "__exported_thumbnail_name__"
+EXPORTER_ARCHIVE_NAME = "__exported_archive_name__"
diff --git a/src/documents/signals/__init__.py b/src/documents/signals/__init__.py
index 810f14f49..393630008 100644
--- a/src/documents/signals/__init__.py
+++ b/src/documents/signals/__init__.py
@@ -1,5 +1,5 @@
from django.dispatch import Signal
-document_consumption_started = Signal(providing_args=["filename"])
-document_consumption_finished = Signal(providing_args=["document"])
-document_consumer_declaration = Signal(providing_args=[])
+document_consumption_started = Signal()
+document_consumption_finished = Signal()
+document_consumer_declaration = Signal()
diff --git a/src/documents/signals/handlers.py b/src/documents/signals/handlers.py
old mode 100644
new mode 100755
index cdeaaba40..586897585
--- a/src/documents/signals/handlers.py
+++ b/src/documents/signals/handlers.py
@@ -6,55 +6,141 @@ from django.conf import settings
from django.contrib.admin.models import ADDITION, LogEntry
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
+from django.db import models, DatabaseError
+from django.db.models import Q
+from django.dispatch import receiver
from django.utils import timezone
+from filelock import FileLock
+from rest_framework.reverse import reverse
-from ..models import Correspondent, Document, Tag
+from .. import index, matching
+from ..file_handling import delete_empty_directories, \
+ create_source_path_directory, archive_name_from_filename, \
+ generate_unique_filename
+from ..models import Document, Tag
def logger(message, group):
logging.getLogger(__name__).debug(message, extra={"group": group})
-def set_correspondent(sender, document=None, logging_group=None, **kwargs):
+def add_inbox_tags(sender, document=None, logging_group=None, **kwargs):
+ inbox_tags = Tag.objects.filter(is_inbox_tag=True)
+ document.tags.add(*inbox_tags)
- # No sense in assigning a correspondent when one is already set.
- if document.correspondent:
+
+def set_correspondent(sender,
+ document=None,
+ logging_group=None,
+ classifier=None,
+ replace=False,
+ use_first=True,
+ **kwargs):
+ if document.correspondent and not replace:
return
- # No matching correspondents, so no need to continue
- potential_correspondents = list(Correspondent.match_all(document.content))
- if not potential_correspondents:
- return
+ potential_correspondents = matching.match_correspondents(document.content,
+ classifier)
potential_count = len(potential_correspondents)
- selected = potential_correspondents[0]
+ if potential_correspondents:
+ selected = potential_correspondents[0]
+ else:
+ selected = None
if potential_count > 1:
- message = "Detected {} potential correspondents, so we've opted for {}"
+ if use_first:
+ logger(
+ f"Detected {potential_count} potential correspondents, "
+ f"so we've opted for {selected}",
+ logging_group
+ )
+ else:
+ logger(
+ f"Detected {potential_count} potential correspondents, "
+ f"not assigning any correspondent",
+ logging_group
+ )
+ return
+
+ if selected or replace:
logger(
- message.format(potential_count, selected),
+ f"Assigning correspondent {selected} to {document}",
logging_group
)
- logger(
- 'Assigning correspondent "{}" to "{}" '.format(selected, document),
- logging_group
- )
-
- document.correspondent = selected
- document.save(update_fields=("correspondent",))
+ document.correspondent = selected
+ document.save(update_fields=("correspondent",))
-def set_tags(sender, document=None, logging_group=None, **kwargs):
+def set_document_type(sender,
+ document=None,
+ logging_group=None,
+ classifier=None,
+ replace=False,
+ use_first=True,
+ **kwargs):
+ if document.document_type and not replace:
+ return
+
+ potential_document_type = matching.match_document_types(document.content,
+ classifier)
+
+ potential_count = len(potential_document_type)
+ if potential_document_type:
+ selected = potential_document_type[0]
+ else:
+ selected = None
+
+ if potential_count > 1:
+ if use_first:
+ logger(
+ f"Detected {potential_count} potential document types, "
+ f"so we've opted for {selected}",
+ logging_group
+ )
+ else:
+ logger(
+ f"Detected {potential_count} potential document types, "
+ f"not assigning any document type",
+ logging_group
+ )
+ return
+
+ if selected or replace:
+ logger(
+ f"Assigning document type {selected} to {document}",
+ logging_group
+ )
+
+ document.document_type = selected
+ document.save(update_fields=("document_type",))
+
+
+def set_tags(sender,
+ document=None,
+ logging_group=None,
+ classifier=None,
+ replace=False,
+ **kwargs):
+
+ if replace:
+ Document.tags.through.objects.filter(document=document).exclude(
+ Q(tag__is_inbox_tag=True)).exclude(
+ Q(tag__match="") & ~Q(tag__matching_algorithm=Tag.MATCH_AUTO)
+ ).delete()
current_tags = set(document.tags.all())
- relevant_tags = set(Tag.match_all(document.content)) - current_tags
+
+ matched_tags = matching.match_tags(document.content, classifier)
+
+ relevant_tags = set(matched_tags) - current_tags
if not relevant_tags:
return
message = 'Tagging "{}" with "{}"'
logger(
- message.format(document, ", ".join([t.slug for t in relevant_tags])),
+ message.format(document, ", ".join([t.name for t in relevant_tags])),
logging_group
)
@@ -76,27 +162,165 @@ def run_post_consume_script(sender, document, **kwargs):
Popen((
settings.POST_CONSUME_SCRIPT,
- str(document.id),
- document.file_name,
- document.source_path,
- document.thumbnail_path,
- document.download_url,
- document.thumbnail_url,
+ str(document.pk),
+ document.get_public_filename(),
+ os.path.normpath(document.source_path),
+ os.path.normpath(document.thumbnail_path),
+ reverse("document-download", kwargs={"pk": document.pk}),
+ reverse("document-thumb", kwargs={"pk": document.pk}),
str(document.correspondent),
- str(",".join(document.tags.all().values_list("slug", flat=True)))
+ str(",".join(document.tags.all().values_list("name", flat=True)))
)).wait()
+@receiver(models.signals.post_delete, sender=Document)
def cleanup_document_deletion(sender, instance, using, **kwargs):
+ with FileLock(settings.MEDIA_LOCK):
+ for f in (instance.source_path,
+ instance.archive_path,
+ instance.thumbnail_path):
+ if os.path.isfile(f):
+ try:
+ os.unlink(f)
+ logging.getLogger(__name__).debug(
+ f"Deleted file {f}.")
+ except OSError as e:
+ logging.getLogger(__name__).warning(
+ f"While deleting document {str(instance)}, the file "
+ f"{f} could not be deleted: {e}"
+ )
- if not isinstance(instance, Document):
+ delete_empty_directories(
+ os.path.dirname(instance.source_path),
+ root=settings.ORIGINALS_DIR
+ )
+
+ delete_empty_directories(
+ os.path.dirname(instance.archive_path),
+ root=settings.ARCHIVE_DIR
+ )
+
+
+def validate_move(instance, old_path, new_path):
+ if not os.path.isfile(old_path):
+ # Can't do anything if the old file does not exist anymore.
+ logging.getLogger(__name__).fatal(
+ f"Document {str(instance)}: File {old_path} has gone.")
+ return False
+
+ if os.path.isfile(new_path):
+ # Can't do anything if the new file already exists. Skip updating file.
+ logging.getLogger(__name__).warning(
+ f"Document {str(instance)}: Cannot rename file "
+ f"since target path {new_path} already exists.")
+ return False
+
+ return True
+
+
+@receiver(models.signals.m2m_changed, sender=Document.tags.through)
+@receiver(models.signals.post_save, sender=Document)
+def update_filename_and_move_files(sender, instance, **kwargs):
+
+ if not instance.filename:
+ # Can't update the filename if there is no filename to begin with
+ # This happens when the consumer creates a new document.
+ # The document is modified and saved multiple times, and only after
+ # everything is done (i.e., the generated filename is final),
+ # filename will be set to the location where the consumer has put
+ # the file.
+ #
+ # This will in turn cause this logic to move the file where it belongs.
return
- for f in (instance.source_path, instance.thumbnail_path):
+ with FileLock(settings.MEDIA_LOCK):
+ old_filename = instance.filename
+ new_filename = generate_unique_filename(
+ instance, settings.ORIGINALS_DIR)
+
+ if new_filename == instance.filename:
+ # Don't do anything if its the same.
+ return
+
+ old_source_path = instance.source_path
+ new_source_path = os.path.join(settings.ORIGINALS_DIR, new_filename)
+
+ if not validate_move(instance, old_source_path, new_source_path):
+ return
+
+ # archive files are optional, archive checksum tells us if we have one,
+ # since this is None for documents without archived files.
+ if instance.archive_checksum:
+ new_archive_filename = archive_name_from_filename(new_filename)
+ old_archive_path = instance.archive_path
+ new_archive_path = os.path.join(settings.ARCHIVE_DIR,
+ new_archive_filename)
+
+ if not validate_move(instance, old_archive_path, new_archive_path):
+ return
+
+ create_source_path_directory(new_archive_path)
+ else:
+ old_archive_path = None
+ new_archive_path = None
+
+ create_source_path_directory(new_source_path)
+
try:
- os.unlink(f)
- except FileNotFoundError:
- pass # The file's already gone, so we're cool with it.
+ os.rename(old_source_path, new_source_path)
+ if instance.archive_checksum:
+ os.rename(old_archive_path, new_archive_path)
+ instance.filename = new_filename
+
+ # Don't save() here to prevent infinite recursion.
+ Document.objects.filter(pk=instance.pk).update(
+ filename=new_filename)
+
+ logging.getLogger(__name__).debug(
+ f"Moved file {old_source_path} to {new_source_path}.")
+
+ if instance.archive_checksum:
+ logging.getLogger(__name__).debug(
+ f"Moved file {old_archive_path} to {new_archive_path}.")
+
+ except OSError as e:
+ instance.filename = old_filename
+ # this happens when we can't move a file. If that's the case for
+ # the archive file, we try our best to revert the changes.
+ # no need to save the instance, the update() has not happened yet.
+ try:
+ os.rename(new_source_path, old_source_path)
+ os.rename(new_archive_path, old_archive_path)
+ except Exception as e:
+ # This is fine, since:
+ # A: if we managed to move source from A to B, we will also
+ # manage to move it from B to A. If not, we have a serious
+ # issue that's going to get caught by the santiy checker.
+ # All files remain in place and will never be overwritten,
+ # so this is not the end of the world.
+ # B: if moving the orignal file failed, nothing has changed
+ # anyway.
+ pass
+ except DatabaseError as e:
+ # this happens after moving files, so move them back into place.
+ # since moving them once succeeded, it's very likely going to
+ # succeed again.
+ os.rename(new_source_path, old_source_path)
+ if instance.archive_checksum:
+ os.rename(new_archive_path, old_archive_path)
+ instance.filename = old_filename
+ # again, no need to save the instance, since the actual update()
+ # operation failed.
+
+ # finally, remove any empty sub folders. This will do nothing if
+ # something has failed above.
+ if not os.path.isfile(old_source_path):
+ delete_empty_directories(os.path.dirname(old_source_path),
+ root=settings.ORIGINALS_DIR)
+
+ if old_archive_path and not os.path.isfile(old_archive_path):
+ delete_empty_directories(os.path.dirname(old_archive_path),
+ root=settings.ARCHIVE_DIR)
def set_log_entry(sender, document=None, logging_group=None, **kwargs):
@@ -108,7 +332,11 @@ def set_log_entry(sender, document=None, logging_group=None, **kwargs):
action_flag=ADDITION,
action_time=timezone.now(),
content_type=ct,
- object_id=document.id,
+ object_id=document.pk,
user=user,
object_repr=document.__str__(),
)
+
+
+def add_to_index(sender, document, **kwargs):
+ index.add_or_update_document(document)
diff --git a/src/documents/static/bootstrap.min.css b/src/documents/static/bootstrap.min.css
new file mode 100644
index 000000000..286cde4c0
--- /dev/null
+++ b/src/documents/static/bootstrap.min.css
@@ -0,0 +1,7 @@
+/*!
+ * Bootstrap v4.5.3 (https://getbootstrap.com/)
+ * Copyright 2011-2020 The Bootstrap Authors
+ * Copyright 2011-2020 Twitter, Inc.
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/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:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--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:transparent}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;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[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;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}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;text-decoration:none;background-color:transparent}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([class]){color:inherit;text-decoration:none}a:not([href]):not([class]):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{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit;text-align:-webkit-match-parent}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;font-weight:300;line-height:1.2}.display-2{font-size:5.5rem;font-weight:300;line-height:1.2}.display-3{font-size:4.5rem;font-weight:300;line-height:1.2}.display-4{font-size:3.5rem;font-weight:300;line-height:1.2}hr{margin-top:1rem;margin-bottom:1rem;border:0;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-unstyled{padding-left:0;list-style:none}.list-inline{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{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem;max-width:100%;height:auto}.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,.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:576px){.container,.container-sm{max-width:540px}}@media (min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media (min-width:992px){.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:1140px}}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;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-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.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-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.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-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.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-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.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-auto{position:relative;width:100%;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-sm-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-sm-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-sm-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-sm-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-sm-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-sm-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-md-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-md-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-md-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-md-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-md-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-md-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-lg-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-lg-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-lg-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-lg-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-lg-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-lg-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.row-cols-xl-1>*{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.row-cols-xl-2>*{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.row-cols-xl-3>*{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.row-cols-xl-4>*{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.row-cols-xl-5>*{-ms-flex:0 0 20%;flex:0 0 20%;max-width:20%}.row-cols-xl-6>*{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:100%}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.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{border:1px solid #dee2e6}.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{background-color:#9fcdff}.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{background-color:#c8cbcf}.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{background-color:#b1dfbb}.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{background-color:#abdde5}.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{background-color:#ffe8a1}.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{background-color:#f1b0b7}.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{background-color:#ececf6}.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{background-color:#b9bbbe}.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th{background-color:rgba(0,0,0,.075)}.table-hover .table-active:hover{background-color:rgba(0,0,0,.075)}.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:rgba(255,255,255,.05)}.table-dark.table-hover tbody tr:hover{color:#fff;background-color:rgba(255,255,255,.075)}@media (max-width:575.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:767.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:991.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::-webkit-input-placeholder{color:#6c757d;opacity:1}.form-control::-moz-placeholder{color:#6c757d;opacity:1}.form-control:-ms-input-placeholder{color:#6c757d;opacity:1}.form-control::-ms-input-placeholder{color:#6c757d;opacity:1}.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{-webkit-appearance:none;-moz-appearance:none;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]{height:auto}textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;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:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;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%;left:0;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,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 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,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 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,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 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,.was-validated .custom-control-input:valid:focus:not(:checked)~.custom-control-label::before{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label,.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%;left:0;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,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%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,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 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,%3csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='none' stroke='%23dc3545' viewBox='0 0 12 12'%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,.was-validated .custom-control-input:invalid:focus:not(:checked)~.custom-control-label::before{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label,.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:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;margin-bottom:0}.form-inline .form-group{display:-ms-flexbox;display:flex;-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center;margin-bottom:0}.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:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;-ms-flex-negative:0;flex-shrink:0;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack: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;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;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:hover{color:#fff;background-color:#0069d9;border-color:#0062cc}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#0069d9;border-color:#0062cc;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:hover{color:#fff;background-color:#5a6268;border-color:#545b62}.btn-secondary.focus,.btn-secondary:focus{color:#fff;background-color:#5a6268;border-color:#545b62;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:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#218838;border-color:#1e7e34;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:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#138496;border-color:#117a8b;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:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{color:#212529;background-color:#e0a800;border-color:#d39e00;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:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c82333;border-color:#bd2130;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:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{color:#212529;background-color:#e2e6ea;border-color:#dae0e5;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:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{color:#fff;background-color:#23272b;border-color:#1d2124;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;text-decoration:underline}.btn-link.focus,.btn-link:focus{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:576px){.dropdown-menu-sm-left{right:auto;left:0}.dropdown-menu-sm-right{right:0;left:auto}}@media (min-width:768px){.dropdown-menu-md-left{right:auto;left:0}.dropdown-menu-md-right{right:0;left:auto}}@media (min-width:992px){.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:""}.dropleft .dropdown-toggle::after{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:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:1 1 auto;flex:1 1 auto}.btn-group-vertical>.btn:hover,.btn-group>.btn:hover{z-index:1}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus{z-index:1}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;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{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;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 input[type=checkbox],.btn-group-toggle>.btn input[type=radio],.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control,.input-group>.form-control-plaintext{position:relative;-ms-flex:1 1 auto;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:-ms-flexbox;display:flex;-ms-flex-align:center;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:-ms-flexbox;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:-ms-flexbox;display:flex;-ms-flex-align:center;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;z-index:1;display:block;min-height:1.5rem;padding-left:1.5rem;-webkit-print-color-adjust:exact;color-adjust:exact}.custom-control-inline{display:-ms-inline-flexbox;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{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;pointer-events:none;content:"";background-color:#fff;border:#adb5bd solid 1px}.custom-control-label::after{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:"";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,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8' viewBox='0 0 8 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,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='4' viewBox='0 0 4 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,%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:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .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;-webkit-transform:translateX(.75rem);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,%3csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5' viewBox='0 0 4 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;-webkit-appearance:none;-moz-appearance:none;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{position:relative;display:inline-block;width:100%;height:calc(1.5em + .75rem + 2px);margin-bottom:0}.custom-file-input{position:relative;z-index:2;width:100%;height:calc(1.5em + .75rem + 2px);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{position:absolute;top:0;right:0;left:0;z-index:1;height:calc(1.5em + .75rem + 2px);padding:.375rem .75rem;font-weight:400;line-height:1.5;color:#495057;background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label::after{position:absolute;top:0;right:0;bottom:0;z-index:3;display:block;height:calc(1.5em + .75rem);padding:.375rem .75rem;line-height:1.5;color:#495057;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;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:0}.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;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{-webkit-transition:none;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;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{-moz-transition:none;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;-ms-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;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{-ms-transition:none;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{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px;background-color:#dee2e6;border-radius:1rem}.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:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;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,.nav-fill>.nav-link{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;padding:.5rem 1rem}.navbar .container,.navbar .container-fluid,.navbar .container-lg,.navbar .container-md,.navbar .container-sm,.navbar .container-xl{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;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:-ms-flexbox;display:flex;-ms-flex-direction:column;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{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;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 center center;background-size:100% 100%}@media (max-width:575.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:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;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{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.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:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;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{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.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:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;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{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;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{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;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{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;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{-ms-flex-direction:row;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{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand{color:rgba(0,0,0,.9)}.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,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.5%29' 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{color:rgba(0,0,0,.9)}.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand{color:#fff}.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:rgba(255,255,255,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:rgba(255,255,255,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:rgba(255,255,255,.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:rgba(255,255,255,.5);border-color:rgba(255,255,255,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.5%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.navbar-dark .navbar-text{color:rgba(255,255,255,.5)}.navbar-dark .navbar-text a{color:#fff}.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;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>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;min-height:1px;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem;margin-bottom:0}.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-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-right:-.625rem;margin-bottom:-.75rem;margin-left:-.625rem;border-bottom:0}.card-header-pills{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem;border-radius:calc(.25rem - 1px)}.card-img,.card-img-bottom,.card-img-top{-ms-flex-negative:0;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:576px){.card-deck{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{-ms-flex:1 0 0%;flex:1 0 0%;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;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:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion{overflow-anchor:none}.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{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item{display:-ms-flexbox;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}.breadcrumb-item+.breadcrumb-item:hover::before{text-decoration:none}.breadcrumb-item.active{color:#6c757d}.pagination{display:-ms-flexbox;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:576px){.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;z-index:2;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}@-webkit-keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:1rem 0}to{background-position:0 0}}.progress{display:-ms-flexbox;display:flex;height:1rem;overflow:hidden;line-height:0;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress-bar{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;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,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:progress-bar-stripes 1s linear infinite;animation:progress-bar-stripes 1s linear infinite}@media (prefers-reduced-motion:reduce){.progress-bar-animated{-webkit-animation:none;animation:none}}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;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{-ms-flex-direction:row;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:576px){.list-group-horizontal-sm{-ms-flex-direction:row;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:768px){.list-group-horizontal-md{-ms-flex-direction:row;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:992px){.list-group-horizontal-lg{-ms-flex-direction:row;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{-ms-flex-direction:row;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{-ms-flex-preferred-size:350px;flex-basis:350px;max-width:350px;font-size:.875rem;background-color:rgba(255,255,255,.85);background-clip:padding-box;border:1px solid rgba(0,0,0,.1);box-shadow:0 .25rem .75rem rgba(0,0,0,.1);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:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.25rem .75rem;color:#6c757d;background-color:rgba(255,255,255,.85);background-clip:padding-box;border-bottom:1px solid rgba(0,0,0,.05);border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.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:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translate(0,-50px);transform:translate(0,-50px)}@media (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:none;transform:none}.modal.modal-static .modal-dialog{-webkit-transform:scale(1.02);transform:scale(1.02)}.modal-dialog-scrollable{display:-ms-flexbox;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{-ms-flex-negative:0;flex-shrink:0}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered::before{display:block;height:calc(100vh - 1rem);height:-webkit-min-content;height:-moz-min-content;height:min-content;content:""}.modal-dialog-centered.modal-dialog-scrollable{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;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:-ms-flexbox;display:flex;-ms-flex-direction:column;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:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem 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 1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;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:576px){.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:-webkit-min-content;height:-moz-min-content;height:min-content}.modal-sm{max-width:300px}}@media (min-width:992px){.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{position:absolute;top:0;left:0;z-index:1060;display:block;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 .arrow{position:absolute;display:block;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 .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 .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{-ms-touch-action:pan-y;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%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-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){-webkit-transform:translateX(100%);transform:translateX(100%)}.active.carousel-item-left,.carousel-item-prev:not(.carousel-item-right){-webkit-transform:translateX(-100%);transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;-webkit-transform:none;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:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack: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,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 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,%3csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' width='8' height='8' viewBox='0 0 8 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:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{box-sizing:content-box;-ms-flex:0 1 auto;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}@-webkit-keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes spinner-border{to{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.spinner-border{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;border:.25em solid currentColor;border-right-color:transparent;border-radius:50%;-webkit-animation:spinner-border .75s linear infinite;animation:spinner-border .75s linear infinite}.spinner-border-sm{width:1rem;height:1rem;border-width:.2em}@-webkit-keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}@keyframes spinner-grow{0%{-webkit-transform:scale(0);transform:scale(0)}50%{opacity:1;-webkit-transform:none;transform:none}}.spinner-grow{display:inline-block;width:2rem;height:2rem;vertical-align:text-bottom;background-color:currentColor;border-radius:50%;opacity:0;-webkit-animation:spinner-grow .75s linear infinite;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;border-top-right-radius:.25rem!important}.rounded-right{border-top-right-radius:.25rem!important;border-bottom-right-radius:.25rem!important}.rounded-bottom{border-bottom-right-radius:.25rem!important;border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important;border-bottom-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:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.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:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.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:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.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:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;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:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;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:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;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.857143%}.embed-responsive-16by9::before{padding-top:56.25%}.embed-responsive-4by3::before{padding-top:75%}.embed-responsive-1by1::before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.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{-webkit-user-select:all!important;-moz-user-select:all!important;-ms-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;-ms-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;-ms-user-select:none!important;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:-webkit-sticky!important;position:sticky!important}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;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:576px){.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:768px){.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:992px){.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:rgba(0,0,0,0)}.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:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.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:rgba(255,255,255,.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-break:break-word!important;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}body{min-width:992px!important}.container{min-width:992px!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}}
+/*# sourceMappingURL=bootstrap.min.css.map */
\ No newline at end of file
diff --git a/src/documents/static/documents/img/gif.png b/src/documents/static/documents/img/gif.png
deleted file mode 120000
index 4a0532b3c..000000000
--- a/src/documents/static/documents/img/gif.png
+++ /dev/null
@@ -1 +0,0 @@
-image.png
\ No newline at end of file
diff --git a/src/documents/static/documents/img/image.png b/src/documents/static/documents/img/image.png
deleted file mode 100644
index 8437a2983..000000000
Binary files a/src/documents/static/documents/img/image.png and /dev/null differ
diff --git a/src/documents/static/documents/img/jpg.png b/src/documents/static/documents/img/jpg.png
deleted file mode 120000
index 4a0532b3c..000000000
--- a/src/documents/static/documents/img/jpg.png
+++ /dev/null
@@ -1 +0,0 @@
-image.png
\ No newline at end of file
diff --git a/src/documents/static/documents/img/pdf.png b/src/documents/static/documents/img/pdf.png
deleted file mode 100644
index 6fdfae993..000000000
Binary files a/src/documents/static/documents/img/pdf.png and /dev/null differ
diff --git a/src/documents/static/documents/img/png.png b/src/documents/static/documents/img/png.png
deleted file mode 120000
index 4a0532b3c..000000000
--- a/src/documents/static/documents/img/png.png
+++ /dev/null
@@ -1 +0,0 @@
-image.png
\ No newline at end of file
diff --git a/src/documents/static/documents/img/tiff.png b/src/documents/static/documents/img/tiff.png
deleted file mode 120000
index 4a0532b3c..000000000
--- a/src/documents/static/documents/img/tiff.png
+++ /dev/null
@@ -1 +0,0 @@
-image.png
\ No newline at end of file
diff --git a/src/documents/static/js/colours.js b/src/documents/static/js/colours.js
deleted file mode 100644
index 41907ead3..000000000
--- a/src/documents/static/js/colours.js
+++ /dev/null
@@ -1,66 +0,0 @@
-// The following jQuery snippet will add a small square next to the selection
-// drop-down on the `Add tag` page that will update to show the selected tag
-// color as the drop-down value is changed.
-
-django.jQuery(document).ready(function(){
-
- if (django.jQuery("#id_colour").length) {
-
- let colour;
- let colour_num;
-
- colour_num = django.jQuery("#id_colour").val() - 1;
- colour = django.jQuery('#id_colour')[0][colour_num].text;
- django.jQuery('#id_colour').after('
');
-
- django.jQuery('.colour_square').css({
- 'float': 'left',
- 'width': '20px',
- 'height': '20px',
- 'margin': '5px',
- 'border': '1px solid rgba(0, 0, 0, .2)',
- 'background': colour
- });
-
- django.jQuery('#id_colour').change(function () {
- colour_num = django.jQuery("#id_colour").val() - 1;
- colour = django.jQuery('#id_colour')[0][colour_num].text;
- django.jQuery('.colour_square').css({'background': colour});
- });
-
- } else if (django.jQuery("select[id*='colour']").length) {
-
- django.jQuery('select[id*="-colour"]').each(function (index, element) {
- let id;
- let loop_colour_num;
- let loop_colour;
-
- id = "colour_square_" + index;
- django.jQuery(element).after('
');
-
- loop_colour_num = django.jQuery(element).val() - 1;
- loop_colour = django.jQuery(element)[0][loop_colour_num].text;
-
- django.jQuery("").appendTo("head");
- django.jQuery('#' + id).css({'background': loop_colour});
-
- console.log(id, loop_colour_num, loop_colour);
-
- django.jQuery(element).change(function () {
- loop_colour_num = django.jQuery(element).val() - 1;
- loop_colour = django.jQuery(element)[0][loop_colour_num].text;
- django.jQuery('#' + id).css({'background': loop_colour});
- console.log('#' + id, loop_colour)
- });
- })
-
- }
-
-});
diff --git a/src/documents/static/paperless.css b/src/documents/static/paperless.css
deleted file mode 100644
index 506debb6d..000000000
--- a/src/documents/static/paperless.css
+++ /dev/null
@@ -1,23 +0,0 @@
-th.column-document,
-td.field-document {
- text-align: center;
-}
-
-td a.tag {
- padding: 0 0.5em;
- color: #ffffff;
- border-radius: 0.2em;
- margin: 1px;
- display: inline-block;
-}
-
-#result_list th.column-note {
- text-align: right;
-}
-#result_list td.field-note {
- text-align: right;
-}
-#result_list td textarea {
- width: 90%;
- height: 5em;
-}
\ No newline at end of file
diff --git a/src/documents/static/signin.css b/src/documents/static/signin.css
new file mode 100644
index 000000000..7441e0394
--- /dev/null
+++ b/src/documents/static/signin.css
@@ -0,0 +1,44 @@
+html,
+body {
+ height: 100%;
+}
+
+body {
+ display: -ms-flexbox;
+ display: flex;
+ -ms-flex-align: center;
+ align-items: center;
+ padding-top: 40px;
+ padding-bottom: 40px;
+ background-color: #f5f5f5;
+}
+
+.form-signin {
+ width: 100%;
+ max-width: 330px;
+ padding: 15px;
+ margin: auto;
+}
+.form-signin .checkbox {
+ font-weight: 400;
+}
+.form-signin .form-control {
+ position: relative;
+ box-sizing: border-box;
+ height: auto;
+ padding: 10px;
+ font-size: 16px;
+}
+.form-signin .form-control:focus {
+ z-index: 2;
+}
+.form-signin input[type="text"] {
+ margin-bottom: -1px;
+ border-bottom-right-radius: 0;
+ border-bottom-left-radius: 0;
+}
+.form-signin input[type="password"] {
+ margin-bottom: 10px;
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
diff --git a/src/documents/tasks.py b/src/documents/tasks.py
new file mode 100644
index 000000000..8c9b00dd6
--- /dev/null
+++ b/src/documents/tasks.py
@@ -0,0 +1,89 @@
+import logging
+
+import tqdm
+from django.conf import settings
+from whoosh.writing import AsyncWriter
+
+from documents import index, sanity_checker
+from documents.classifier import DocumentClassifier, \
+ IncompatibleClassifierVersionError
+from documents.consumer import Consumer, ConsumerError
+from documents.models import Document
+from documents.sanity_checker import SanityFailedError
+
+
+def index_optimize():
+ ix = index.open_index()
+ writer = AsyncWriter(ix)
+ writer.commit(optimize=True)
+
+
+def index_reindex():
+ documents = Document.objects.all()
+
+ ix = index.open_index(recreate=True)
+
+ with AsyncWriter(ix) as writer:
+ for document in tqdm.tqdm(documents):
+ index.update_document(writer, document)
+
+
+def train_classifier():
+ classifier = DocumentClassifier()
+
+ try:
+ # load the classifier, since we might not have to train it again.
+ classifier.reload()
+ except (FileNotFoundError, IncompatibleClassifierVersionError):
+ # This is what we're going to fix here.
+ pass
+
+ try:
+ if classifier.train():
+ logging.getLogger(__name__).info(
+ "Saving updated classifier model to {}...".format(
+ settings.MODEL_FILE)
+ )
+ classifier.save_classifier()
+ else:
+ logging.getLogger(__name__).debug(
+ "Training data unchanged."
+ )
+
+ except Exception as e:
+ logging.getLogger(__name__).error(
+ "Classifier error: " + str(e)
+ )
+
+
+def consume_file(path,
+ override_filename=None,
+ override_title=None,
+ override_correspondent_id=None,
+ override_document_type_id=None,
+ override_tag_ids=None):
+
+ document = Consumer().try_consume_file(
+ path,
+ override_filename=override_filename,
+ override_title=override_title,
+ override_correspondent_id=override_correspondent_id,
+ override_document_type_id=override_document_type_id,
+ override_tag_ids=override_tag_ids)
+
+ if document:
+ return "Success. New document id {} created".format(
+ document.pk
+ )
+ else:
+ raise ConsumerError("Unknown error: Returned document was null, but "
+ "no error message was given.")
+
+
+def sanity_check():
+ messages = sanity_checker.check_sanity()
+
+ if len(messages) > 0:
+ raise SanityFailedError(messages)
+ else:
+ return "No issues detected."
diff --git a/src/documents/templates/admin/base_site.html b/src/documents/templates/admin/base_site.html
deleted file mode 100644
index 0f76ec586..000000000
--- a/src/documents/templates/admin/base_site.html
+++ /dev/null
@@ -1,93 +0,0 @@
-{% extends 'admin/base_site.html' %}
-
-{# NOTE: This should probably be extending base.html. See CSS comment below details. #}
-
-
-{% load static %}
-{% load custom_css from customisation %}
-{% load custom_js from customisation %}
-
-
-{% block extrahead %}
-
-
-{% endblock %}
-
-
-{% block branding %}
-
-
-
-{% endblock %}
-
-
-{% block blockbots %}
-
- {% comment %}
- This really should be extending `extrastyle`, but the the
- django-flat-responsive package decided that it wanted to put its CSS in
- this block, so to make sure that overrides are in fact overriding
- everything else, we have to do the Wrong Thing here.
-
- Once we switch to Django 2.x and drop django-flat-responsive, we should
- switch this to `extrastyle` where it should be.
- {% endcomment %}
-
- {{ block.super }}
-
- {% custom_css %}
-
-{% endblock blockbots %}
-
-
-{% block footer %}
-
- {% comment %}
- The Django admin doesn't have a block for Javascript you'd want placed in
- the footer, so we have to use this one instead.
- {% endcomment %}
-
- {{ block.super }}
-
- {% custom_js %}
-
-{% endblock footer %}
diff --git a/src/documents/templates/admin/documents/document/change_form.html b/src/documents/templates/admin/documents/document/change_form.html
deleted file mode 100644
index 6edcbf2b1..000000000
--- a/src/documents/templates/admin/documents/document/change_form.html
+++ /dev/null
@@ -1,64 +0,0 @@
-{% extends 'admin/change_form.html' %}
-
-{% block content %}
-
-{{ block.super }}
-
-
Preview
-
-
-
-{% if next_object %}
-
-{% endif %}
-
-{% endblock content %}
-
-{% block extrastyle %}
-{{ block.super }}
-
-{% endblock %}
-
-{% block footer %}
-
- {{ block.super }}
-
- {# Hack to force Django to make the created date a date input rather than `text` (the default) #}
-
-
-{% endblock footer %}
diff --git a/src/documents/templates/admin/documents/document/change_list.html b/src/documents/templates/admin/documents/document/change_list.html
deleted file mode 100644
index dcfb8a979..000000000
--- a/src/documents/templates/admin/documents/document/change_list.html
+++ /dev/null
@@ -1,12 +0,0 @@
-{% extends 'admin/change_list.html' %}
-
-
-{% load admin_actions from admin_list%}
-{% load result_list from hacks %}
-
-
-{% block result_list %}
- {% if action_form and actions_on_top and cl.show_admin_actions %}{% admin_actions %}{% endif %}
- {% result_list cl %}
- {% if action_form and actions_on_bottom and cl.show_admin_actions %}{% admin_actions %}{% endif %}
-{% endblock %}
diff --git a/src/documents/templates/admin/documents/document/change_list_results.html b/src/documents/templates/admin/documents/document/change_list_results.html
deleted file mode 100644
index c52e9bed8..000000000
--- a/src/documents/templates/admin/documents/document/change_list_results.html
+++ /dev/null
@@ -1,194 +0,0 @@
-{% load i18n %}
-
-
-
-
-{# This is just copypasta from the parent change_list_results.html file #}
-
-
-
-{% for header in result_headers %}
-
- {% if header.sortable %}
- {% if header.sort_priority > 0 %}
-
-
- {% if num_sorted_fields > 1 %}
{{ header.sort_priority }} {% endif %}
-
-
- {% endif %}
- {% endif %}
- {% if header.sortable %}
{{ header.text|capfirst }} {% else %}
{{ header.text|capfirst }} {% endif %}
-
- {% endfor %}
-
-
-
-{# /copypasta #}
-
-
-
- {% for result in results %}
- {# 0: Checkbox #}
- {# 1: Title #}
- {# 2: Date #}
- {# 3: Added #}
- {# 4: Image #}
- {# 5: Correspondent #}
- {# 6: Tags #}
- {# 7: Document edit url #}
-
-
-
-
{{ result.6 }}
-
{{ result.2 }}
-
-
{{ result.4 }}
-
-
- {% endfor %}
-
-
-
-
diff --git a/src/documents/templates/admin/documents/document/select_object.html b/src/documents/templates/admin/documents/document/select_object.html
deleted file mode 100644
index 775d57b12..000000000
--- a/src/documents/templates/admin/documents/document/select_object.html
+++ /dev/null
@@ -1,50 +0,0 @@
-{% extends "admin/base_site.html" %}
-
-
-{% load i18n l10n admin_urls static %}
-{% load staticfiles %}
-
-
-{% block extrahead %}
- {{ block.super }}
- {{ media }}
-
-{% endblock %}
-
-
-{% block bodyclass %}{{ block.super }} app-{{ opts.app_label }} model-{{ opts.model_name }} delete-confirmation delete-selected-confirmation{% endblock %}
-
-
-{% block breadcrumbs %}
-
-{% endblock %}
-
-{% block content %}
- Please select the {{itemname}}.
- {% csrf_token %}
-
- {% for obj in queryset %}
-
- {% endfor %}
-
-
- {% for obj in objects %}
- {{ obj.name }}
- {% endfor %}
-
-
-
-
-
-
-
- {% trans "Go back" %}
-
-
-
-{% endblock %}
diff --git a/src/documents/templates/admin/index.html b/src/documents/templates/admin/index.html
deleted file mode 100644
index 410c9e684..000000000
--- a/src/documents/templates/admin/index.html
+++ /dev/null
@@ -1,57 +0,0 @@
-{% extends "admin/index.html" %}
-
-
-{% load i18n static %}
-
-
-{# This block adds a search form on the admin start page and on the module start page so that #}
-{# the user can quickly search for documents #}
-{% block pretitle %}
-
-
{% trans 'Search documents' %}
-
-
-
-{% endblock %}
-
-
-{# This whole block is here just to override the `get_admin_log` line so #}
-{# that the log entries aren't limited to the current user #}
-{% block sidebar %}
-
-
-
{% trans 'Recent actions' %}
-
{% trans 'My actions' %}
- {% load log %}
- {% get_admin_log 10 as admin_log %}
- {% if not admin_log %}
-
{% trans 'None available' %}
- {% else %}
-
- {% for entry in admin_log %}
-
- {% if entry.is_deletion or not entry.get_admin_url %}
- {{ entry.object_repr }}
- {% else %}
- {{ entry.object_repr }}
- {% endif %}
-
- {% if entry.content_type %}
- {% filter capfirst %}{{ entry.content_type }}{% endfilter %}
- {% else %}
- {% trans 'Unknown content' %}
- {% endif %}
-
- {% endfor %}
-
- {% endif %}
-
-
-{% endblock %}
diff --git a/src/documents/templates/documents/index.html b/src/documents/templates/documents/index.html
deleted file mode 100644
index c0f58b115..000000000
--- a/src/documents/templates/documents/index.html
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
- Paperless
-
-
-
- {# One day someone (maybe even myself) is going to write a proper web front-end for Paperless, and this is where it'll start. #}
-
-
diff --git a/src/documents/templates/index.html b/src/documents/templates/index.html
new file mode 100644
index 000000000..728f3a0e7
--- /dev/null
+++ b/src/documents/templates/index.html
@@ -0,0 +1,19 @@
+
+
+{% load static %}
+
+
+
+
+ PaperlessUi
+
+
+
+
+
+ Loading...
+
+
+
+
+
diff --git a/src/documents/templates/registration/logged_out.html b/src/documents/templates/registration/logged_out.html
new file mode 100644
index 000000000..4a3cb60bb
--- /dev/null
+++ b/src/documents/templates/registration/logged_out.html
@@ -0,0 +1,44 @@
+
+
+{% load static %}
+
+
+
+
+
+
+
+
+ Paperless Sign In
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/documents/templates/registration/login.html b/src/documents/templates/registration/login.html
new file mode 100644
index 000000000..25a707bed
--- /dev/null
+++ b/src/documents/templates/registration/login.html
@@ -0,0 +1,54 @@
+
+
+{% load static %}
+
+
+
+
+
+
+
+
+ Paperless Sign In
+
+
+
+
+
+
+
+
+
+
+
+ {% csrf_token %}
+
+ Please sign in.
+ {% if form.errors %}
+
+ Your username and password didn't match. Please try again.
+
+ {% endif %}
+ Username
+
+ Password
+
+ Sign in
+
+
+
diff --git a/src/documents/templatetags/customisation.py b/src/documents/templatetags/customisation.py
deleted file mode 100644
index 793fd0eb3..000000000
--- a/src/documents/templatetags/customisation.py
+++ /dev/null
@@ -1,37 +0,0 @@
-import os
-
-from django import template
-from django.conf import settings
-from django.utils.safestring import mark_safe
-
-register = template.Library()
-
-
-@register.simple_tag()
-def custom_css():
- theme_path = os.path.join(
- settings.MEDIA_ROOT,
- "overrides.css"
- )
- if os.path.exists(theme_path):
- return mark_safe(
- ' '.format(
- os.path.join(settings.MEDIA_URL, "overrides.css")
- )
- )
- return ""
-
-
-@register.simple_tag()
-def custom_js():
- theme_path = os.path.join(
- settings.MEDIA_ROOT,
- "overrides.js"
- )
- if os.path.exists(theme_path):
- return mark_safe(
- ''.format(
- os.path.join(settings.MEDIA_URL, "overrides.js")
- )
- )
- return ""
diff --git a/src/documents/templatetags/hacks.py b/src/documents/templatetags/hacks.py
deleted file mode 100644
index 0c0a0e099..000000000
--- a/src/documents/templatetags/hacks.py
+++ /dev/null
@@ -1,43 +0,0 @@
-import re
-
-from django.contrib.admin.templatetags.admin_list import (
- result_headers,
- result_hidden_fields,
- results
-)
-from django.template import Library
-
-
-EXTRACT_URL = re.compile(r'href="(.*?)"')
-
-register = Library()
-
-
-@register.inclusion_tag("admin/documents/document/change_list_results.html")
-def result_list(cl):
- """
- Copy/pasted from django.contrib.admin.templatetags.admin_list just so I can
- modify the value passed to `.inclusion_tag()` in the decorator here. There
- must be a cleaner way... right?
- """
- headers = list(result_headers(cl))
- num_sorted_fields = 0
- for h in headers:
- if h['sortable'] and h['sorted']:
- num_sorted_fields += 1
- return {'cl': cl,
- 'result_hidden_fields': list(result_hidden_fields(cl)),
- 'result_headers': headers,
- 'num_sorted_fields': num_sorted_fields,
- 'results': map(add_doc_edit_url, results(cl))}
-
-
-def add_doc_edit_url(result):
- """
- Make the document edit URL accessible to the view as a separate item
- """
- title = result[1]
- match = re.search(EXTRACT_URL, title)
- edit_doc_url = match.group(1)
- result.append(edit_doc_url)
- return result
diff --git a/src/paperless_tesseract/tests/samples/no-text.png b/src/documents/tests/examples/no-text.png
similarity index 100%
rename from src/paperless_tesseract/tests/samples/no-text.png
rename to src/documents/tests/examples/no-text.png
diff --git a/src/documents/tests/factories.py b/src/documents/tests/factories.py
index 8ed747b83..243821751 100644
--- a/src/documents/tests/factories.py
+++ b/src/documents/tests/factories.py
@@ -1,17 +1,18 @@
-import factory
+from factory import Faker
+from factory.django import DjangoModelFactory
from ..models import Document, Correspondent
-class CorrespondentFactory(factory.DjangoModelFactory):
+class CorrespondentFactory(DjangoModelFactory):
class Meta:
model = Correspondent
- name = factory.Faker("name")
+ name = Faker("name")
-class DocumentFactory(factory.DjangoModelFactory):
+class DocumentFactory(DjangoModelFactory):
class Meta:
model = Document
diff --git a/src/documents/tests/samples/documents/archive/0000001.pdf b/src/documents/tests/samples/documents/archive/0000001.pdf
new file mode 100644
index 000000000..ea08363bf
Binary files /dev/null and b/src/documents/tests/samples/documents/archive/0000001.pdf differ
diff --git a/src/documents/tests/samples/documents/originals/0000001.pdf b/src/documents/tests/samples/documents/originals/0000001.pdf
new file mode 100644
index 000000000..e450de482
Binary files /dev/null and b/src/documents/tests/samples/documents/originals/0000001.pdf differ
diff --git a/src/documents/tests/samples/documents/originals/0000002.pdf.gpg b/src/documents/tests/samples/documents/originals/0000002.pdf.gpg
new file mode 100644
index 000000000..0322a8039
Binary files /dev/null and b/src/documents/tests/samples/documents/originals/0000002.pdf.gpg differ
diff --git a/src/documents/tests/samples/documents/thumbnails/0000001.png b/src/documents/tests/samples/documents/thumbnails/0000001.png
new file mode 100644
index 000000000..a3a768401
Binary files /dev/null and b/src/documents/tests/samples/documents/thumbnails/0000001.png differ
diff --git a/src/documents/tests/samples/documents/thumbnails/0000002.png.gpg b/src/documents/tests/samples/documents/thumbnails/0000002.png.gpg
new file mode 100644
index 000000000..8a61a9126
Binary files /dev/null and b/src/documents/tests/samples/documents/thumbnails/0000002.png.gpg differ
diff --git a/src/documents/tests/samples/inline_mail.txt b/src/documents/tests/samples/inline_mail.txt
deleted file mode 100644
index 549014b53..000000000
--- a/src/documents/tests/samples/inline_mail.txt
+++ /dev/null
@@ -1,8044 +0,0 @@
-Return-Path:
-Received: from mout.mailhost.com ([212.227.17.22]) by mx-ha.mailhost.com (mx101) with
- ESMTPS (Nemesis) id 0M22yt-1bePTC1Tkx-00u3T3 for ; Thu,
- 14 Apr 2016 16:26:53 +0200
-Received: from [192.168.178.11] ([65.50.20.247]) by mail.mailhost.com (mr101)
- with ESMTPSA (Nemesis) id 0MB2G8-1ayNjv3pA4-009xHi for ;
- Thu, 14 Apr 2016 16:26:53 +0200
-From: Florian Harr
-Content-Type: multipart/alternative; boundary="Apple-Mail=_E0BA9ABB-7689-4507-B940-34AA2DDB3DCC"
-Subject: Paperless Inline Image
-Message-Id:
-Date: Thu, 14 Apr 2016 10:26:36 -0400
-To: Florian Harr
-Mime-Version: 1.0 (Mac OS X Mail 9.3 \(3124\))
-X-Mailer: Apple Mail (2.3124)
-Envelope-To:
-
-
---Apple-Mail=_E0BA9ABB-7689-4507-B940-34AA2DDB3DCC
-Content-Transfer-Encoding: 7BIT
-Content-Type: TEXT/plain;
- charset=us-ascii
-
-The secret word is "paperless" :-)
-
-
---Apple-Mail=_E0BA9ABB-7689-4507-B940-34AA2DDB3DCC
-Content-Type: multipart/related;
- boundary="Apple-Mail=_4D0B5162-526C-4D33-95B0-7037924B5757";
- type="text/html"
-
-
---Apple-Mail=_4D0B5162-526C-4D33-95B0-7037924B5757
-Content-Transfer-Encoding: 7BIT
-Content-Type: TEXT/html;
- charset=us-ascii
-
-The secret word is "paperless" :-)
---Apple-Mail=_4D0B5162-526C-4D33-95B0-7037924B5757
-Content-Transfer-Encoding: BASE64
-Content-Disposition: inline;
- filename=screenshot.png
-Content-Type: IMAGE/png;
- name=screenshot.png;
- x-unix-mode=0600
-Content-Id: <8536D9B0-0634-4B01-8584-BE7C49F413E3>
-
-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
-0lDXS5xzNM2eNvbTxaKM13HAGMUw2PiQfdQqZzoel3UC7HaHeHxmZvNlDKDE3E2fSYzhuhFQWZnN
-UfvkrtPeCyAj442argPp2wIWBm8hSDJrVMArg9IG1DYDGQAAIABJREFUbzUEiydQ1JZKl6zKJevF
-knfv3nFzcyMgfbtFG482nq5rWCykjTcXq+iUXKJUAWFEq2RaMdAPntG1eC3XalUXFD6W72o9lcbO
-4JtBKYvRHqOLWTtzIcn/PCYM080njX2ngPJRG8tVHK/5GYw6vRHmY8Dpzet0fM5B9u8LHWuRVQRR
-VOTmke4LKDQKo4WRZrXBKI1Rcj0vNmUcP0qMTm0RKMu4KufcBPx5n/qQyBh471kWF0c3ybzdpP91
-cZxMN3UYhnFadJEEWxOCw3uFc+DcXD4r/VduyMk9XRJ64n4fa/6dMr8+BgQ9Ni6ffvb0npxr8CWQ
-uKoqFguRb0jM3LRvj4Eief9IGqEJDM3Hazn/Mzjl3cfLYD90jLNUwwy85hOix8DJD8VjK5ynfTtv
-o8c+n4eLSKbSWT/OSrJDLLWX9Yc4qVTxb4W42j/SBqfg7wfDfNhQJYQg24zmW2k/JoN0LUDgcRz/
-bayO10WYriP3gTnBnzL6vuc3v/kNZVnyb/7Nv6FpGv72b/+W7XaLMYavv/6axWLBy5cvp7Ydx5Hf
-/va37Pd7vvjiC1arFe/evePNmzdcX1+z2Wz46U9/ilKKf/fv/p3MB7znzZs3EyiYJCPu7u548+YN
-T58+5fnz5zjn+Id/+AeGYeCLL75AJAhaLi8vJ3f4qqp48+YN+/2eFy9eYIzhu+++48mTJzx//py2
-bfnmm28YhoEf//jHjOPI/f092+1W7jGbDX/7t39L0zRcXFzExb0t//E//ke+/PJL/uW//Jfc3d3x
-u9/9jmfPnvHq1atpfjMMA7/+9a8ZhoEXL14QgkjmfP311wD86Ec/om1bXr9+Tdd1U1XAzc0NXdex
-3W55+fIlm82G29tbfvvb33J1dcVyueTq6or7+3u++eYbnj17xsuXL48A1nOc4xznOMc5/hwjnyPm
-INt//+///YhEFoKQ07766it+/vOf88knn/DJJ5/w5s2b6b7a9z2/+MUvcM7xV3/1V1RVxfv379nv
-93z22Wfs93t+9atf8dlnn/Hs2TO++eYbtNY0jcilffnll9zc3PCLX/yCf/7P/znb7Zarq6tpvvGT
-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
---Apple-Mail=_4D0B5162-526C-4D33-95B0-7037924B5757--
-
---Apple-Mail=_E0BA9ABB-7689-4507-B940-34AA2DDB3DCC--
diff --git a/src/documents/tests/samples/mail.txt b/src/documents/tests/samples/mail.txt
deleted file mode 100644
index b7712eee5..000000000
--- a/src/documents/tests/samples/mail.txt
+++ /dev/null
@@ -1,208 +0,0 @@
-Return-Path:
-X-Original-To: sender@mailbox4.mailhost.com
-Delivered-To: sender@mailbox4.mailhost.com
-Received: from mx8.mailhost.com (mail8.mailhost.com [75.126.24.68])
- by mailbox4.mailhost.com (Postfix) with ESMTP id B62BD5498001
- for ; Thu, 4 Feb 2016 22:01:17 +0000 (UTC)
-Received: from localhost (localhost.localdomain [127.0.0.1])
- by mx8.mailhost.com (Postfix) with ESMTP id B41796F190D
- for ; Thu, 4 Feb 2016 22:01:17 +0000 (UTC)
-X-Spam-Flag: NO
-X-Spam-Score: 0
-X-Spam-Level:
-X-Spam-Status: No, score=0 tagged_above=-999 required=3
- tests=[RCVD_IN_DNSWL_NONE=-0.0001]
-Received: from mx8.mailhost.com ([127.0.0.1])
- by localhost (mail8.mailhost.com [127.0.0.1]) (amavisd-new, port 10024)
- with ESMTP id 3cj6d28FXsS3 for ;
- Thu, 4 Feb 2016 22:01:17 +0000 (UTC)
-Received: from smtp.mailhost.com (smtp.mailhost.com [74.55.86.74])
- by mx8.mailhost.com (Postfix) with ESMTP id 527D76F1529
- for ; Thu, 4 Feb 2016 22:01:17 +0000 (UTC)
-Received: from [10.114.0.19] (nl3x.mullvad.net [46.166.136.162])
- by smtp.mailhost.com (Postfix) with ESMTP id 9C52420C6FDA
- for ; Thu, 4 Feb 2016 22:01:16 +0000 (UTC)
-To: paperless@example.com
-From: Daniel Quinn
-Subject: Test 0
-Message-ID: <56B3CA2A.6030806@example.com>
-Date: Thu, 4 Feb 2016 22:01:14 +0000
-User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101
- Thunderbird/38.5.0
-MIME-Version: 1.0
-Content-Type: multipart/mixed;
- boundary="------------090701020702030809070008"
-
-This is a multi-part message in MIME format.
---------------090701020702030809070008
-Content-Type: text/plain; charset=utf-8
-Content-Transfer-Encoding: 7bit
-
-The secret word is "paperless" :-)
-
---------------090701020702030809070008
-Content-Type: application/pdf;
- name="test0.pdf"
-Content-Transfer-Encoding: base64
-Content-Disposition: attachment;
- filename="test0.pdf"
-
-JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0
-ZURlY29kZT4+CnN0cmVhbQp4nFWLQQvCMAyF7/kVOQutSdeuHZSA0+3gbVDwIN6c3gR38e/b
-bF4kkPfyvReyjB94IyFVF7pgG0ze4TLDZYevLamzPKEvEFqbMEZfq+WO+5GRHZbHNROLy+So
-UfFi6g7/RyusEpUl9VsQxQTlHR2oV3wUEzOdhOnXG1aw/o1yK2cYCkww4RdbUCevCmVuZHN0
-cmVhbQplbmRvYmoKCjMgMCBvYmoKMTM5CmVuZG9iagoKNSAwIG9iago8PC9MZW5ndGggNiAw
-IFIvRmlsdGVyL0ZsYXRlRGVjb2RlL0xlbmd0aDEgMTA4MjQ+PgpzdHJlYW0KeJzlOWt0G9WZ
-95uRbNmWLckPWY4SaRTFedmybI8T4rw8sS3ZiZ1YfqWSCbFkS7YEtiQkJSE8GlNeOQ5pUmh5
-Zkt2l+XQNl3GhLaBpcWw0D19UGALLRRS0gM9nD0lxVBK9wCx97tXI0UJAc727L8d+c587/u9
-7p0rOZXYEyJaMkV4Io1OBuLOqmqBEPJLQqB0dG9K2NRTsQHhM4Rw/zkWH5+870e7PiRE9Rgh
-+Y+NT+wf+/b3e4YI0YYJKX41HAoEfxj6vUjIIgltrA0jYef8/nzEr0F8WXgydY2bP7QO8WOI
-SxOx0cDxxbUmxN9AfOlk4Jr4apWLI8SMKBGigcmQpYXrRBx9KtobjyVTQbJsgZDl91B+PBGK
-d9838hzipwjhjyIN8EMvLYJ5FOd4lTovX1NQWKQtLtGR/3eX+jCpIJ3qTURH4ux+wcWfIFXk
-XkIW3qXY+ft898LH/5deaNKPe8hD5DFymLxGrlAYbuIhEbIHKbnX0+QlpNLLQ4bId8n055g9
-QU4hPy3nJ0doJJe8PORucpL8xwWzeMgkuQ59+QF5DRrIz7BVYuQD0JAbyXNo9QOkbb+UKa4E
-b2MMHMuhvk7u5w6RbdzbiNxLOZyT05NnyTHYjZZTGOfhbMQbP2P0NnID3vtJmOxFmF3qTZ/+
-jhQs/AWjuoFsI18jW8hEjsaT8ABfiPUbIA9gTp9mNGeGmd/JX8n9kOPO3YnIN8g4jgBg7Nxh
-fsvnZOh/ffGDpBhW8dWk4FJcrono5j/mGhc+5JeRQjK4MJehLXQt/IUPzEdVw6rF6k2qX3zR
-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
-Fpt7cad50ChWDBpAN6gXdYMcYKFFMujULeg4nW5Yd0DH60gL4aaMoIZTcHRmoL+mputU/kJf
-l6zxXC7DQbm6n96l3iE576BMBocu984AfN13y+HDpHVJl9zY75X9S3xdchABiQJTCOiXzBhJ
-qy+ZTNWwC2pqEN6Dd1KzpwaJu5NpKsnySU0SkrhHJZkS1FCBNA54r6E8JFA9QO3dSUJvlFmT
-VqLaScUcU07fGGDa/T/LhW2oCmVuZHN0cmVhbQplbmRvYmoKCjYgMCBvYmoKNjI5MQplbmRv
-YmoKCjcgMCBvYmoKPDwvVHlwZS9Gb250RGVzY3JpcHRvci9Gb250TmFtZS9CQUFBQUErTGli
-ZXJhdGlvblNlcmlmCi9GbGFncyA0Ci9Gb250QkJveFstNTQzIC0zMDMgMTI3NyA5ODFdL0l0
-YWxpY0FuZ2xlIDAKL0FzY2VudCA4OTEKL0Rlc2NlbnQgLTIxNgovQ2FwSGVpZ2h0IDk4MQov
-U3RlbVYgODAKL0ZvbnRGaWxlMiA1IDAgUgo+PgplbmRvYmoKCjggMCBvYmoKPDwvTGVuZ3Ro
-IDI5Mi9GaWx0ZXIvRmxhdGVEZWNvZGU+PgpzdHJlYW0KeJxdkctuwyAQRfd8Bct0EfmROA/J
-spQmseRFH6rbD3BgnCLVGGGy8N+XmUlbqQvQmZl7BxiSY3NqrAnJqx9VC0H2xmoP03jzCuQF
-rsaKLJfaqHCPaFdD50QSve08BRga249lKZK3WJuCn+XioMcLPIjkxWvwxl7l4uPYxri9OfcF
-A9ggU1FVUkMf+zx17rkbICHXstGxbMK8jJY/wfvsQOYUZ3wVNWqYXKfAd/YKokzTSpZ1XQmw
-+l8tK9hy6dVn56M0i9I0LdZV5Jx4s0NeMe+R18TbFXJBnKfIG9ZkyFvWUJ8d5wvkPTPlD8w1
-8iMz9Tyyl/Qnzp+Qz8xn5JrPPdOj7rfH5+H8f8Ym1c37ODL6JJoVTslY+P1HNzp00foG7l+O
-gwplbmRzdHJlYW0KZW5kb2JqCgo5IDAgb2JqCjw8L1R5cGUvRm9udC9TdWJ0eXBlL1RydWVU
-eXBlL0Jhc2VGb250L0JBQUFBQStMaWJlcmF0aW9uU2VyaWYKL0ZpcnN0Q2hhciAwCi9MYXN0
-Q2hhciAxNQovV2lkdGhzWzc3NyA2MTAgNTAwIDI3NyAzODkgMjUwIDQ0MyAyNzcgNDQzIDUw
-MCA1MDAgNDQzIDUwMCA3NzcgNTAwIDI1MApdCi9Gb250RGVzY3JpcHRvciA3IDAgUgovVG9V
-bmljb2RlIDggMCBSCj4+CmVuZG9iagoKMTAgMCBvYmoKPDwvRjEgOSAwIFIKPj4KZW5kb2Jq
-CgoxMSAwIG9iago8PC9Gb250IDEwIDAgUgovUHJvY1NldFsvUERGL1RleHRdCj4+CmVuZG9i
-agoKMSAwIG9iago8PC9UeXBlL1BhZ2UvUGFyZW50IDQgMCBSL1Jlc291cmNlcyAxMSAwIFIv
-TWVkaWFCb3hbMCAwIDU5NSA4NDJdL0dyb3VwPDwvUy9UcmFuc3BhcmVuY3kvQ1MvRGV2aWNl
-UkdCL0kgdHJ1ZT4+L0NvbnRlbnRzIDIgMCBSPj4KZW5kb2JqCgo0IDAgb2JqCjw8L1R5cGUv
-UGFnZXMKL1Jlc291cmNlcyAxMSAwIFIKL01lZGlhQm94WyAwIDAgNTk1IDg0MiBdCi9LaWRz
-WyAxIDAgUiBdCi9Db3VudCAxPj4KZW5kb2JqCgoxMiAwIG9iago8PC9UeXBlL0NhdGFsb2cv
-UGFnZXMgNCAwIFIKL09wZW5BY3Rpb25bMSAwIFIgL1hZWiBudWxsIG51bGwgMF0KL0xhbmco
-ZW4tR0IpCj4+CmVuZG9iagoKMTMgMCBvYmoKPDwvQ3JlYXRvcjxGRUZGMDA1NzAwNzIwMDY5
-MDA3NDAwNjUwMDcyPgovUHJvZHVjZXI8RkVGRjAwNEMwMDY5MDA2MjAwNzIwMDY1MDA0RjAw
-NjYwMDY2MDA2OTAwNjMwMDY1MDAyMDAwMzUwMDJFMDAzMD4KL0NyZWF0aW9uRGF0ZShEOjIw
-MTYwMjA0MjIwMDAyWicpPj4KZW5kb2JqCgp4cmVmCjAgMTQKMDAwMDAwMDAwMCA2NTUzNSBm
-IAowMDAwMDA3NTA5IDAwMDAwIG4gCjAwMDAwMDAwMTkgMDAwMDAgbiAKMDAwMDAwMDIyOSAw
-MDAwMCBuIAowMDAwMDA3NjUyIDAwMDAwIG4gCjAwMDAwMDAyNDkgMDAwMDAgbiAKMDAwMDAw
-NjYyNSAwMDAwMCBuIAowMDAwMDA2NjQ2IDAwMDAwIG4gCjAwMDAwMDY4NDEgMDAwMDAgbiAK
-MDAwMDAwNzIwMiAwMDAwMCBuIAowMDAwMDA3NDIyIDAwMDAwIG4gCjAwMDAwMDc0NTQgMDAw
-MDAgbiAKMDAwMDAwNzc1MSAwMDAwMCBuIAowMDAwMDA3ODQ4IDAwMDAwIG4gCnRyYWlsZXIK
-PDwvU2l6ZSAxNC9Sb290IDEyIDAgUgovSW5mbyAxMyAwIFIKL0lEIFsgPDRFN0ZCMEZCMjA4
-ODBCNURBQkIzQTNEOTQxNDlBRTQ3Pgo8NEU3RkIwRkIyMDg4MEI1REFCQjNBM0Q5NDE0OUFF
-NDc+IF0KL0RvY0NoZWNrc3VtIC8yQTY0RDMzNzRFQTVEODMwNTRDNEI2RDFEMUY4QzU1RQo+
-PgpzdGFydHhyZWYKODAxOAolJUVPRgo=
---------------090701020702030809070008--
diff --git a/src/documents/tests/samples/simple.pdf b/src/documents/tests/samples/simple.pdf
new file mode 100644
index 000000000..e450de482
Binary files /dev/null and b/src/documents/tests/samples/simple.pdf differ
diff --git a/src/documents/tests/samples/simple.zip b/src/documents/tests/samples/simple.zip
new file mode 100644
index 000000000..e96270508
Binary files /dev/null and b/src/documents/tests/samples/simple.zip differ
diff --git a/src/documents/tests/test_api.py b/src/documents/tests/test_api.py
new file mode 100644
index 000000000..49dddee87
--- /dev/null
+++ b/src/documents/tests/test_api.py
@@ -0,0 +1,597 @@
+import os
+import shutil
+import tempfile
+from unittest import mock
+
+from django.contrib.auth.models import User
+from rest_framework.test import APITestCase
+from whoosh.writing import AsyncWriter
+
+from documents import index
+from documents.models import Document, Correspondent, DocumentType, Tag, SavedView
+from documents.tests.utils import DirectoriesMixin
+
+
+class TestDocumentApi(DirectoriesMixin, APITestCase):
+
+ def setUp(self):
+ super(TestDocumentApi, self).setUp()
+
+ self.user = User.objects.create_superuser(username="temp_admin")
+ self.client.force_login(user=self.user)
+
+ def testDocuments(self):
+
+ response = self.client.get("/api/documents/").data
+
+ self.assertEqual(response['count'], 0)
+
+ c = Correspondent.objects.create(name="c", pk=41)
+ dt = DocumentType.objects.create(name="dt", pk=63)
+ tag = Tag.objects.create(name="t", pk=85)
+
+ doc = Document.objects.create(title="WOW", content="the content", correspondent=c, document_type=dt, checksum="123", mime_type="application/pdf")
+
+ doc.tags.add(tag)
+
+ response = self.client.get("/api/documents/", format='json')
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.data['count'], 1)
+
+ returned_doc = response.data['results'][0]
+ self.assertEqual(returned_doc['id'], doc.id)
+ self.assertEqual(returned_doc['title'], doc.title)
+ self.assertEqual(returned_doc['correspondent'], c.id)
+ self.assertEqual(returned_doc['document_type'], dt.id)
+ self.assertListEqual(returned_doc['tags'], [tag.id])
+
+ c2 = Correspondent.objects.create(name="c2")
+
+ returned_doc['correspondent'] = c2.pk
+ returned_doc['title'] = "the new title"
+
+ response = self.client.put('/api/documents/{}/'.format(doc.pk), returned_doc, format='json')
+
+ self.assertEqual(response.status_code, 200)
+
+ doc_after_save = Document.objects.get(id=doc.id)
+
+ self.assertEqual(doc_after_save.correspondent, c2)
+ self.assertEqual(doc_after_save.title, "the new title")
+
+ self.client.delete("/api/documents/{}/".format(doc_after_save.pk))
+
+ self.assertEqual(len(Document.objects.all()), 0)
+
+ def test_document_actions(self):
+
+ _, filename = tempfile.mkstemp(dir=self.dirs.originals_dir)
+
+ content = b"This is a test"
+ content_thumbnail = b"thumbnail content"
+
+ with open(filename, "wb") as f:
+ f.write(content)
+
+ doc = Document.objects.create(title="none", filename=os.path.basename(filename), mime_type="application/pdf")
+
+ with open(os.path.join(self.dirs.thumbnail_dir, "{:07d}.png".format(doc.pk)), "wb") as f:
+ f.write(content_thumbnail)
+
+ response = self.client.get('/api/documents/{}/download/'.format(doc.pk))
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, content)
+
+ response = self.client.get('/api/documents/{}/preview/'.format(doc.pk))
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, content)
+
+ response = self.client.get('/api/documents/{}/thumb/'.format(doc.pk))
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, content_thumbnail)
+
+ def test_download_with_archive(self):
+
+ _, filename = tempfile.mkstemp(dir=self.dirs.originals_dir)
+
+ content = b"This is a test"
+ content_archive = b"This is the same test but archived"
+
+ with open(filename, "wb") as f:
+ f.write(content)
+
+ filename = os.path.basename(filename)
+
+ doc = Document.objects.create(title="none", filename=filename,
+ mime_type="application/pdf")
+
+ with open(doc.archive_path, "wb") as f:
+ f.write(content_archive)
+
+ response = self.client.get('/api/documents/{}/download/'.format(doc.pk))
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, content_archive)
+
+ response = self.client.get('/api/documents/{}/download/?original=true'.format(doc.pk))
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, content)
+
+ response = self.client.get('/api/documents/{}/preview/'.format(doc.pk))
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, content_archive)
+
+ response = self.client.get('/api/documents/{}/preview/?original=true'.format(doc.pk))
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, content)
+
+ def test_document_actions_not_existing_file(self):
+
+ doc = Document.objects.create(title="none", filename=os.path.basename("asd"), mime_type="application/pdf")
+
+ response = self.client.get('/api/documents/{}/download/'.format(doc.pk))
+ self.assertEqual(response.status_code, 404)
+
+ response = self.client.get('/api/documents/{}/preview/'.format(doc.pk))
+ self.assertEqual(response.status_code, 404)
+
+ response = self.client.get('/api/documents/{}/thumb/'.format(doc.pk))
+ self.assertEqual(response.status_code, 404)
+
+ def test_document_filters(self):
+
+ doc1 = Document.objects.create(title="none1", checksum="A", mime_type="application/pdf")
+ doc2 = Document.objects.create(title="none2", checksum="B", mime_type="application/pdf")
+ doc3 = Document.objects.create(title="none3", checksum="C", mime_type="application/pdf")
+
+ tag_inbox = Tag.objects.create(name="t1", is_inbox_tag=True)
+ tag_2 = Tag.objects.create(name="t2")
+ tag_3 = Tag.objects.create(name="t3")
+
+ doc1.tags.add(tag_inbox)
+ doc2.tags.add(tag_2)
+ doc3.tags.add(tag_2)
+ doc3.tags.add(tag_3)
+
+ response = self.client.get("/api/documents/?is_in_inbox=true")
+ self.assertEqual(response.status_code, 200)
+ results = response.data['results']
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['id'], doc1.id)
+
+ response = self.client.get("/api/documents/?is_in_inbox=false")
+ self.assertEqual(response.status_code, 200)
+ results = response.data['results']
+ self.assertEqual(len(results), 2)
+ self.assertCountEqual([results[0]['id'], results[1]['id']], [doc2.id, doc3.id])
+
+ response = self.client.get("/api/documents/?tags__id__in={},{}".format(tag_inbox.id, tag_3.id))
+ self.assertEqual(response.status_code, 200)
+ results = response.data['results']
+ self.assertEqual(len(results), 2)
+ self.assertCountEqual([results[0]['id'], results[1]['id']], [doc1.id, doc3.id])
+
+ response = self.client.get("/api/documents/?tags__id__all={},{}".format(tag_2.id, tag_3.id))
+ self.assertEqual(response.status_code, 200)
+ results = response.data['results']
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['id'], doc3.id)
+
+ response = self.client.get("/api/documents/?tags__id__all={},{}".format(tag_inbox.id, tag_3.id))
+ self.assertEqual(response.status_code, 200)
+ results = response.data['results']
+ self.assertEqual(len(results), 0)
+
+ response = self.client.get("/api/documents/?tags__id__all={}a{}".format(tag_inbox.id, tag_3.id))
+ self.assertEqual(response.status_code, 200)
+ results = response.data['results']
+ self.assertEqual(len(results), 3)
+
+ response = self.client.get("/api/documents/?tags__id__none={}".format(tag_3.id))
+ self.assertEqual(response.status_code, 200)
+ results = response.data['results']
+ self.assertEqual(len(results), 2)
+ self.assertCountEqual([results[0]['id'], results[1]['id']], [doc1.id, doc2.id])
+
+ response = self.client.get("/api/documents/?tags__id__none={},{}".format(tag_3.id, tag_2.id))
+ self.assertEqual(response.status_code, 200)
+ results = response.data['results']
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['id'], doc1.id)
+
+ response = self.client.get("/api/documents/?tags__id__none={},{}".format(tag_2.id, tag_inbox.id))
+ self.assertEqual(response.status_code, 200)
+ results = response.data['results']
+ self.assertEqual(len(results), 0)
+
+ def test_search_no_query(self):
+ response = self.client.get("/api/search/")
+ results = response.data['results']
+
+ self.assertEqual(len(results), 0)
+
+ def test_search(self):
+ d1=Document.objects.create(title="invoice", content="the thing i bought at a shop and paid with bank account", checksum="A", pk=1)
+ d2=Document.objects.create(title="bank statement 1", content="things i paid for in august", pk=2, checksum="B")
+ d3=Document.objects.create(title="bank statement 3", content="things i paid for in september", pk=3, checksum="C")
+ with AsyncWriter(index.open_index()) as writer:
+ # Note to future self: there is a reason we dont use a model signal handler to update the index: some operations edit many documents at once
+ # (retagger, renamer) and we don't want to open a writer for each of these, but rather perform the entire operation with one writer.
+ # That's why we cant open the writer in a model on_save handler or something.
+ index.update_document(writer, d1)
+ index.update_document(writer, d2)
+ index.update_document(writer, d3)
+ response = self.client.get("/api/search/?query=bank")
+ results = response.data['results']
+ self.assertEqual(response.data['count'], 3)
+ self.assertEqual(response.data['page'], 1)
+ self.assertEqual(response.data['page_count'], 1)
+ self.assertEqual(len(results), 3)
+
+ response = self.client.get("/api/search/?query=september")
+ results = response.data['results']
+ self.assertEqual(response.data['count'], 1)
+ self.assertEqual(response.data['page'], 1)
+ self.assertEqual(response.data['page_count'], 1)
+ self.assertEqual(len(results), 1)
+
+ response = self.client.get("/api/search/?query=statement")
+ results = response.data['results']
+ self.assertEqual(response.data['count'], 2)
+ self.assertEqual(response.data['page'], 1)
+ self.assertEqual(response.data['page_count'], 1)
+ self.assertEqual(len(results), 2)
+
+ response = self.client.get("/api/search/?query=sfegdfg")
+ results = response.data['results']
+ self.assertEqual(response.data['count'], 0)
+ self.assertEqual(response.data['page'], 0)
+ self.assertEqual(response.data['page_count'], 0)
+ self.assertEqual(len(results), 0)
+
+ def test_search_multi_page(self):
+ with AsyncWriter(index.open_index()) as writer:
+ for i in range(55):
+ doc = Document.objects.create(checksum=str(i), pk=i+1, title=f"Document {i+1}", content="content")
+ index.update_document(writer, doc)
+
+ # This is here so that we test that no document gets returned twice (might happen if the paging is not working)
+ seen_ids = []
+
+ for i in range(1, 6):
+ response = self.client.get(f"/api/search/?query=content&page={i}")
+ results = response.data['results']
+ self.assertEqual(response.data['count'], 55)
+ self.assertEqual(response.data['page'], i)
+ self.assertEqual(response.data['page_count'], 6)
+ self.assertEqual(len(results), 10)
+
+ for result in results:
+ self.assertNotIn(result['id'], seen_ids)
+ seen_ids.append(result['id'])
+
+ response = self.client.get(f"/api/search/?query=content&page=6")
+ results = response.data['results']
+ self.assertEqual(response.data['count'], 55)
+ self.assertEqual(response.data['page'], 6)
+ self.assertEqual(response.data['page_count'], 6)
+ self.assertEqual(len(results), 5)
+
+ for result in results:
+ self.assertNotIn(result['id'], seen_ids)
+ seen_ids.append(result['id'])
+
+ response = self.client.get(f"/api/search/?query=content&page=7")
+ results = response.data['results']
+ self.assertEqual(response.data['count'], 55)
+ self.assertEqual(response.data['page'], 6)
+ self.assertEqual(response.data['page_count'], 6)
+ self.assertEqual(len(results), 5)
+
+ def test_search_invalid_page(self):
+ with AsyncWriter(index.open_index()) as writer:
+ for i in range(15):
+ doc = Document.objects.create(checksum=str(i), pk=i+1, title=f"Document {i+1}", content="content")
+ index.update_document(writer, doc)
+
+ first_page = self.client.get(f"/api/search/?query=content&page=1").data
+ second_page = self.client.get(f"/api/search/?query=content&page=2").data
+ should_be_first_page_1 = self.client.get(f"/api/search/?query=content&page=0").data
+ should_be_first_page_2 = self.client.get(f"/api/search/?query=content&page=dgfd").data
+ should_be_first_page_3 = self.client.get(f"/api/search/?query=content&page=").data
+ should_be_first_page_4 = self.client.get(f"/api/search/?query=content&page=-7868").data
+
+ self.assertDictEqual(first_page, should_be_first_page_1)
+ self.assertDictEqual(first_page, should_be_first_page_2)
+ self.assertDictEqual(first_page, should_be_first_page_3)
+ self.assertDictEqual(first_page, should_be_first_page_4)
+ self.assertNotEqual(len(first_page['results']), len(second_page['results']))
+
+ @mock.patch("documents.index.autocomplete")
+ def test_search_autocomplete(self, m):
+ m.side_effect = lambda ix, term, limit: [term for _ in range(limit)]
+
+ response = self.client.get("/api/search/autocomplete/?term=test")
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(len(response.data), 10)
+
+ response = self.client.get("/api/search/autocomplete/?term=test&limit=20")
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(len(response.data), 20)
+
+ response = self.client.get("/api/search/autocomplete/?term=test&limit=-1")
+ self.assertEqual(response.status_code, 400)
+
+ response = self.client.get("/api/search/autocomplete/")
+ self.assertEqual(response.status_code, 400)
+
+ response = self.client.get("/api/search/autocomplete/?term=")
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(len(response.data), 10)
+
+ def test_search_spelling_correction(self):
+ with AsyncWriter(index.open_index()) as writer:
+ for i in range(55):
+ doc = Document.objects.create(checksum=str(i), pk=i+1, title=f"Document {i+1}", content=f"Things document {i+1}")
+ index.update_document(writer, doc)
+
+ response = self.client.get("/api/search/?query=thing")
+ correction = response.data['corrected_query']
+
+ self.assertEqual(correction, "things")
+
+ response = self.client.get("/api/search/?query=things")
+ correction = response.data['corrected_query']
+
+ self.assertEqual(correction, None)
+
+ def test_statistics(self):
+
+ doc1 = Document.objects.create(title="none1", checksum="A")
+ doc2 = Document.objects.create(title="none2", checksum="B")
+ doc3 = Document.objects.create(title="none3", checksum="C")
+
+ tag_inbox = Tag.objects.create(name="t1", is_inbox_tag=True)
+
+ doc1.tags.add(tag_inbox)
+
+ response = self.client.get("/api/statistics/")
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.data['documents_total'], 3)
+ self.assertEqual(response.data['documents_inbox'], 1)
+
+ @mock.patch("documents.views.async_task")
+ def test_upload(self, m):
+
+ with open(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), "rb") as f:
+ response = self.client.post("/api/documents/post_document/", {"document": f})
+
+ self.assertEqual(response.status_code, 200)
+
+ m.assert_called_once()
+
+ args, kwargs = m.call_args
+ self.assertEqual(kwargs['override_filename'], "simple.pdf")
+ self.assertIsNone(kwargs['override_title'])
+ self.assertIsNone(kwargs['override_correspondent_id'])
+ self.assertIsNone(kwargs['override_document_type_id'])
+ self.assertIsNone(kwargs['override_tag_ids'])
+
+ @mock.patch("documents.views.async_task")
+ def test_upload_invalid_form(self, m):
+
+ with open(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), "rb") as f:
+ response = self.client.post("/api/documents/post_document/", {"documenst": f})
+ self.assertEqual(response.status_code, 400)
+ m.assert_not_called()
+
+ @mock.patch("documents.views.async_task")
+ def test_upload_invalid_file(self, m):
+
+ with open(os.path.join(os.path.dirname(__file__), "samples", "simple.zip"), "rb") as f:
+ response = self.client.post("/api/documents/post_document/", {"document": f})
+ self.assertEqual(response.status_code, 400)
+ m.assert_not_called()
+
+ @mock.patch("documents.views.async_task")
+ def test_upload_with_title(self, async_task):
+ with open(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), "rb") as f:
+ response = self.client.post("/api/documents/post_document/", {"document": f, "title": "my custom title"})
+ self.assertEqual(response.status_code, 200)
+
+ async_task.assert_called_once()
+
+ args, kwargs = async_task.call_args
+
+ self.assertEqual(kwargs['override_title'], "my custom title")
+
+ @mock.patch("documents.views.async_task")
+ def test_upload_with_correspondent(self, async_task):
+ c = Correspondent.objects.create(name="test-corres")
+ with open(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), "rb") as f:
+ response = self.client.post("/api/documents/post_document/", {"document": f, "correspondent": c.id})
+ self.assertEqual(response.status_code, 200)
+
+ async_task.assert_called_once()
+
+ args, kwargs = async_task.call_args
+
+ self.assertEqual(kwargs['override_correspondent_id'], c.id)
+
+ @mock.patch("documents.views.async_task")
+ def test_upload_with_invalid_correspondent(self, async_task):
+ with open(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), "rb") as f:
+ response = self.client.post("/api/documents/post_document/", {"document": f, "correspondent": 3456})
+ self.assertEqual(response.status_code, 400)
+
+ async_task.assert_not_called()
+
+ @mock.patch("documents.views.async_task")
+ def test_upload_with_document_type(self, async_task):
+ dt = DocumentType.objects.create(name="invoice")
+ with open(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), "rb") as f:
+ response = self.client.post("/api/documents/post_document/", {"document": f, "document_type": dt.id})
+ self.assertEqual(response.status_code, 200)
+
+ async_task.assert_called_once()
+
+ args, kwargs = async_task.call_args
+
+ self.assertEqual(kwargs['override_document_type_id'], dt.id)
+
+ @mock.patch("documents.views.async_task")
+ def test_upload_with_invalid_document_type(self, async_task):
+ with open(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), "rb") as f:
+ response = self.client.post("/api/documents/post_document/", {"document": f, "document_type": 34578})
+ self.assertEqual(response.status_code, 400)
+
+ async_task.assert_not_called()
+
+ @mock.patch("documents.views.async_task")
+ def test_upload_with_tags(self, async_task):
+ t1 = Tag.objects.create(name="tag1")
+ t2 = Tag.objects.create(name="tag2")
+ with open(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), "rb") as f:
+ response = self.client.post(
+ "/api/documents/post_document/",
+ {"document": f, "tags": [t2.id, t1.id]})
+ self.assertEqual(response.status_code, 200)
+
+ async_task.assert_called_once()
+
+ args, kwargs = async_task.call_args
+
+ self.assertCountEqual(kwargs['override_tag_ids'], [t1.id, t2.id])
+
+ @mock.patch("documents.views.async_task")
+ def test_upload_with_invalid_tags(self, async_task):
+ t1 = Tag.objects.create(name="tag1")
+ t2 = Tag.objects.create(name="tag2")
+ with open(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), "rb") as f:
+ response = self.client.post(
+ "/api/documents/post_document/",
+ {"document": f, "tags": [t2.id, t1.id, 734563]})
+ self.assertEqual(response.status_code, 400)
+
+ async_task.assert_not_called()
+
+ def test_get_metadata(self):
+ doc = Document.objects.create(title="test", filename="file.pdf", mime_type="image/png", archive_checksum="A")
+
+ shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "documents", "thumbnails", "0000001.png"), doc.source_path)
+ shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), doc.archive_path)
+
+ response = self.client.get(f"/api/documents/{doc.pk}/metadata/")
+ self.assertEqual(response.status_code, 200)
+
+ meta = response.data
+
+ self.assertEqual(meta['original_mime_type'], "image/png")
+ self.assertTrue(meta['has_archive_version'])
+ self.assertEqual(len(meta['original_metadata']), 0)
+ self.assertGreater(len(meta['archive_metadata']), 0)
+
+ def test_get_metadata_no_archive(self):
+ doc = Document.objects.create(title="test", filename="file.pdf", mime_type="application/pdf")
+
+ shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "simple.pdf"), doc.source_path)
+
+ response = self.client.get(f"/api/documents/{doc.pk}/metadata/")
+ self.assertEqual(response.status_code, 200)
+
+ meta = response.data
+
+ self.assertEqual(meta['original_mime_type'], "application/pdf")
+ self.assertFalse(meta['has_archive_version'])
+ self.assertGreater(len(meta['original_metadata']), 0)
+ self.assertIsNone(meta['archive_metadata'])
+
+ def test_saved_views(self):
+ u1 = User.objects.create_user("user1")
+ u2 = User.objects.create_user("user2")
+
+ v1 = SavedView.objects.create(user=u1, name="test1", sort_field="", show_on_dashboard=False, show_in_sidebar=False)
+ v2 = SavedView.objects.create(user=u2, name="test2", sort_field="", show_on_dashboard=False, show_in_sidebar=False)
+ v3 = SavedView.objects.create(user=u2, name="test3", sort_field="", show_on_dashboard=False, show_in_sidebar=False)
+
+ response = self.client.get("/api/saved_views/")
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.data['count'], 0)
+
+ self.assertEqual(self.client.get(f"/api/saved_views/{v1.id}/").status_code, 404)
+
+ self.client.force_login(user=u1)
+
+ response = self.client.get("/api/saved_views/")
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.data['count'], 1)
+
+ self.assertEqual(self.client.get(f"/api/saved_views/{v1.id}/").status_code, 200)
+
+ self.client.force_login(user=u2)
+
+ response = self.client.get("/api/saved_views/")
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.data['count'], 2)
+
+ self.assertEqual(self.client.get(f"/api/saved_views/{v1.id}/").status_code, 404)
+
+ def test_create_update_patch(self):
+
+ u1 = User.objects.create_user("user1")
+
+ view = {
+ "name": "test",
+ "show_on_dashboard": True,
+ "show_in_sidebar": True,
+ "sort_field": "created2",
+ "filter_rules": [
+ {
+ "rule_type": 4,
+ "value": "test"
+ }
+ ]
+ }
+
+ response = self.client.post("/api/saved_views/", view, format='json')
+ self.assertEqual(response.status_code, 201)
+
+ v1 = SavedView.objects.get(name="test")
+ self.assertEqual(v1.sort_field, "created2")
+ self.assertEqual(v1.filter_rules.count(), 1)
+ self.assertEqual(v1.user, self.user)
+
+ response = self.client.patch(f"/api/saved_views/{v1.id}/", {
+ "show_in_sidebar": False
+ }, format='json')
+
+ v1 = SavedView.objects.get(id=v1.id)
+ self.assertEqual(response.status_code, 200)
+ self.assertFalse(v1.show_in_sidebar)
+ self.assertEqual(v1.filter_rules.count(), 1)
+
+ view['filter_rules'] = [{
+ "rule_type": 12,
+ "value": "secret"
+ }]
+
+ response = self.client.put(f"/api/saved_views/{v1.id}/", view, format='json')
+ self.assertEqual(response.status_code, 200)
+
+ v1 = SavedView.objects.get(id=v1.id)
+ self.assertEqual(v1.filter_rules.count(), 1)
+ self.assertEqual(v1.filter_rules.first().value, "secret")
+
+ view['filter_rules'] = []
+
+ response = self.client.put(f"/api/saved_views/{v1.id}/", view, format='json')
+ self.assertEqual(response.status_code, 200)
+
+ v1 = SavedView.objects.get(id=v1.id)
+ self.assertEqual(v1.filter_rules.count(), 0)
diff --git a/src/documents/tests/test_checks.py b/src/documents/tests/test_checks.py
index da3a4adf0..1027c11a0 100644
--- a/src/documents/tests/test_checks.py
+++ b/src/documents/tests/test_checks.py
@@ -2,9 +2,9 @@ import unittest
from django.test import TestCase
+from .factories import DocumentFactory
from ..checks import changed_password_check
from ..models import Document
-from .factories import DocumentFactory
class ChecksTestCase(TestCase):
@@ -15,11 +15,3 @@ class ChecksTestCase(TestCase):
def test_changed_password_check_no_encryption(self):
DocumentFactory.create(storage_type=Document.STORAGE_TYPE_UNENCRYPTED)
self.assertEqual(changed_password_check(None), [])
-
- @unittest.skip("I don't know how to test this")
- def test_changed_password_check_gpg_encryption_with_good_password(self):
- pass
-
- @unittest.skip("I don't know how to test this")
- def test_changed_password_check_fail(self):
- pass
diff --git a/src/documents/tests/test_classifier.py b/src/documents/tests/test_classifier.py
new file mode 100644
index 000000000..9e999794d
--- /dev/null
+++ b/src/documents/tests/test_classifier.py
@@ -0,0 +1,237 @@
+import tempfile
+from time import sleep
+from unittest import mock
+
+from django.test import TestCase, override_settings
+
+from documents.classifier import DocumentClassifier, IncompatibleClassifierVersionError
+from documents.models import Correspondent, Document, Tag, DocumentType
+from documents.tests.utils import DirectoriesMixin
+
+
+class TestClassifier(DirectoriesMixin, TestCase):
+
+ def setUp(self):
+ super(TestClassifier, self).setUp()
+ self.classifier = DocumentClassifier()
+
+ def generate_test_data(self):
+ self.c1 = Correspondent.objects.create(name="c1", matching_algorithm=Correspondent.MATCH_AUTO)
+ self.c2 = Correspondent.objects.create(name="c2")
+ self.c3 = Correspondent.objects.create(name="c3", matching_algorithm=Correspondent.MATCH_AUTO)
+ self.t1 = Tag.objects.create(name="t1", matching_algorithm=Tag.MATCH_AUTO, pk=12)
+ self.t2 = Tag.objects.create(name="t2", matching_algorithm=Tag.MATCH_ANY, pk=34, is_inbox_tag=True)
+ self.t3 = Tag.objects.create(name="t3", matching_algorithm=Tag.MATCH_AUTO, pk=45)
+ self.dt = DocumentType.objects.create(name="dt", matching_algorithm=DocumentType.MATCH_AUTO)
+ self.dt2 = DocumentType.objects.create(name="dt2", matching_algorithm=DocumentType.MATCH_AUTO)
+
+ self.doc1 = Document.objects.create(title="doc1", content="this is a document from c1", correspondent=self.c1, checksum="A", document_type=self.dt)
+ self.doc2 = Document.objects.create(title="doc1", content="this is another document, but from c2", correspondent=self.c2, checksum="B")
+ self.doc_inbox = Document.objects.create(title="doc235", content="aa", checksum="C")
+
+ self.doc1.tags.add(self.t1)
+ self.doc2.tags.add(self.t1)
+ self.doc2.tags.add(self.t3)
+ self.doc_inbox.tags.add(self.t2)
+
+ def testNoTrainingData(self):
+ try:
+ self.classifier.train()
+ except ValueError as e:
+ self.assertEqual(str(e), "No training data available.")
+ else:
+ self.fail("Should raise exception")
+
+ def testEmpty(self):
+ Document.objects.create(title="WOW", checksum="3457", content="ASD")
+ self.classifier.train()
+ self.assertIsNone(self.classifier.document_type_classifier)
+ self.assertIsNone(self.classifier.tags_classifier)
+ self.assertIsNone(self.classifier.correspondent_classifier)
+
+ self.assertListEqual(self.classifier.predict_tags(""), [])
+ self.assertIsNone(self.classifier.predict_document_type(""))
+ self.assertIsNone(self.classifier.predict_correspondent(""))
+
+ def testTrain(self):
+ self.generate_test_data()
+ self.classifier.train()
+ self.assertListEqual(list(self.classifier.correspondent_classifier.classes_), [-1, self.c1.pk])
+ self.assertListEqual(list(self.classifier.tags_binarizer.classes_), [self.t1.pk, self.t3.pk])
+
+ def testPredict(self):
+ self.generate_test_data()
+ self.classifier.train()
+ self.assertEqual(self.classifier.predict_correspondent(self.doc1.content), self.c1.pk)
+ self.assertEqual(self.classifier.predict_correspondent(self.doc2.content), None)
+ self.assertListEqual(self.classifier.predict_tags(self.doc1.content), [self.t1.pk])
+ self.assertListEqual(self.classifier.predict_tags(self.doc2.content), [self.t1.pk, self.t3.pk])
+ self.assertEqual(self.classifier.predict_document_type(self.doc1.content), self.dt.pk)
+ self.assertEqual(self.classifier.predict_document_type(self.doc2.content), None)
+
+ def testDatasetHashing(self):
+
+ self.generate_test_data()
+
+ self.assertTrue(self.classifier.train())
+ self.assertFalse(self.classifier.train())
+
+ def testVersionIncreased(self):
+
+ self.generate_test_data()
+ self.assertTrue(self.classifier.train())
+ self.assertFalse(self.classifier.train())
+
+ self.classifier.save_classifier()
+
+ classifier2 = DocumentClassifier()
+
+ current_ver = DocumentClassifier.FORMAT_VERSION
+ with mock.patch("documents.classifier.DocumentClassifier.FORMAT_VERSION", current_ver+1):
+ # assure that we won't load old classifiers.
+ self.assertRaises(IncompatibleClassifierVersionError, classifier2.reload)
+
+ self.classifier.save_classifier()
+
+ # assure that we can load the classifier after saving it.
+ classifier2.reload()
+
+ def testReload(self):
+
+ self.generate_test_data()
+ self.assertTrue(self.classifier.train())
+ self.classifier.save_classifier()
+
+ classifier2 = DocumentClassifier()
+ classifier2.reload()
+ v1 = classifier2.classifier_version
+
+ # change the classifier after some time.
+ sleep(1)
+ self.classifier.save_classifier()
+
+ classifier2.reload()
+ v2 = classifier2.classifier_version
+ self.assertNotEqual(v1, v2)
+
+ @override_settings(DATA_DIR=tempfile.mkdtemp())
+ def testSaveClassifier(self):
+
+ self.generate_test_data()
+
+ self.classifier.train()
+
+ self.classifier.save_classifier()
+
+ new_classifier = DocumentClassifier()
+ new_classifier.reload()
+ self.assertFalse(new_classifier.train())
+
+ def test_one_correspondent_predict(self):
+ c1 = Correspondent.objects.create(name="c1", matching_algorithm=Correspondent.MATCH_AUTO)
+ doc1 = Document.objects.create(title="doc1", content="this is a document from c1", correspondent=c1, checksum="A")
+
+ self.classifier.train()
+ self.assertEqual(self.classifier.predict_correspondent(doc1.content), c1.pk)
+
+ def test_one_correspondent_predict_manydocs(self):
+ c1 = Correspondent.objects.create(name="c1", matching_algorithm=Correspondent.MATCH_AUTO)
+ doc1 = Document.objects.create(title="doc1", content="this is a document from c1", correspondent=c1, checksum="A")
+ doc2 = Document.objects.create(title="doc2", content="this is a document from noone", checksum="B")
+
+ self.classifier.train()
+ self.assertEqual(self.classifier.predict_correspondent(doc1.content), c1.pk)
+ self.assertIsNone(self.classifier.predict_correspondent(doc2.content))
+
+ def test_one_type_predict(self):
+ dt = DocumentType.objects.create(name="dt", matching_algorithm=DocumentType.MATCH_AUTO)
+
+ doc1 = Document.objects.create(title="doc1", content="this is a document from c1",
+ checksum="A", document_type=dt)
+
+ self.classifier.train()
+ self.assertEqual(self.classifier.predict_document_type(doc1.content), dt.pk)
+
+ def test_one_type_predict_manydocs(self):
+ dt = DocumentType.objects.create(name="dt", matching_algorithm=DocumentType.MATCH_AUTO)
+
+ doc1 = Document.objects.create(title="doc1", content="this is a document from c1",
+ checksum="A", document_type=dt)
+
+ doc2 = Document.objects.create(title="doc1", content="this is a document from c2",
+ checksum="B")
+
+ self.classifier.train()
+ self.assertEqual(self.classifier.predict_document_type(doc1.content), dt.pk)
+ self.assertIsNone(self.classifier.predict_document_type(doc2.content))
+
+ def test_one_tag_predict(self):
+ t1 = Tag.objects.create(name="t1", matching_algorithm=Tag.MATCH_AUTO, pk=12)
+
+ doc1 = Document.objects.create(title="doc1", content="this is a document from c1", checksum="A")
+
+ doc1.tags.add(t1)
+ self.classifier.train()
+ self.assertListEqual(self.classifier.predict_tags(doc1.content), [t1.pk])
+
+ def test_one_tag_predict_unassigned(self):
+ t1 = Tag.objects.create(name="t1", matching_algorithm=Tag.MATCH_AUTO, pk=12)
+
+ doc1 = Document.objects.create(title="doc1", content="this is a document from c1", checksum="A")
+
+ self.classifier.train()
+ self.assertListEqual(self.classifier.predict_tags(doc1.content), [])
+
+ def test_two_tags_predict_singledoc(self):
+ t1 = Tag.objects.create(name="t1", matching_algorithm=Tag.MATCH_AUTO, pk=12)
+ t2 = Tag.objects.create(name="t2", matching_algorithm=Tag.MATCH_AUTO, pk=121)
+
+ doc4 = Document.objects.create(title="doc1", content="this is a document from c4", checksum="D")
+
+ doc4.tags.add(t1)
+ doc4.tags.add(t2)
+ self.classifier.train()
+ self.assertListEqual(self.classifier.predict_tags(doc4.content), [t1.pk, t2.pk])
+
+ def test_two_tags_predict(self):
+ t1 = Tag.objects.create(name="t1", matching_algorithm=Tag.MATCH_AUTO, pk=12)
+ t2 = Tag.objects.create(name="t2", matching_algorithm=Tag.MATCH_AUTO, pk=121)
+
+ doc1 = Document.objects.create(title="doc1", content="this is a document from c1", checksum="A")
+ doc2 = Document.objects.create(title="doc1", content="this is a document from c2", checksum="B")
+ doc3 = Document.objects.create(title="doc1", content="this is a document from c3", checksum="C")
+ doc4 = Document.objects.create(title="doc1", content="this is a document from c4", checksum="D")
+
+ doc1.tags.add(t1)
+ doc2.tags.add(t2)
+
+ doc4.tags.add(t1)
+ doc4.tags.add(t2)
+ self.classifier.train()
+ self.assertListEqual(self.classifier.predict_tags(doc1.content), [t1.pk])
+ self.assertListEqual(self.classifier.predict_tags(doc2.content), [t2.pk])
+ self.assertListEqual(self.classifier.predict_tags(doc3.content), [])
+ self.assertListEqual(self.classifier.predict_tags(doc4.content), [t1.pk, t2.pk])
+
+ def test_one_tag_predict_multi(self):
+ t1 = Tag.objects.create(name="t1", matching_algorithm=Tag.MATCH_AUTO, pk=12)
+
+ doc1 = Document.objects.create(title="doc1", content="this is a document from c1", checksum="A")
+ doc2 = Document.objects.create(title="doc2", content="this is a document from c2", checksum="B")
+
+ doc1.tags.add(t1)
+ doc2.tags.add(t1)
+ self.classifier.train()
+ self.assertListEqual(self.classifier.predict_tags(doc1.content), [t1.pk])
+ self.assertListEqual(self.classifier.predict_tags(doc2.content), [t1.pk])
+
+ def test_one_tag_predict_multi_2(self):
+ t1 = Tag.objects.create(name="t1", matching_algorithm=Tag.MATCH_AUTO, pk=12)
+
+ doc1 = Document.objects.create(title="doc1", content="this is a document from c1", checksum="A")
+ doc2 = Document.objects.create(title="doc2", content="this is a document from c2", checksum="B")
+
+ doc1.tags.add(t1)
+ self.classifier.train()
+ self.assertListEqual(self.classifier.predict_tags(doc1.content), [t1.pk])
+ self.assertListEqual(self.classifier.predict_tags(doc2.content), [])
diff --git a/src/documents/tests/test_consumer.py b/src/documents/tests/test_consumer.py
index 512447741..b4b19be4c 100644
--- a/src/documents/tests/test_consumer.py
+++ b/src/documents/tests/test_consumer.py
@@ -1,120 +1,57 @@
+import os
import re
-
-from django.test import TestCase
+import shutil
+import tempfile
from unittest import mock
-from tempfile import TemporaryDirectory
+from unittest.mock import MagicMock
-from ..consumer import Consumer
-from ..models import FileInfo, Tag
+from django.test import TestCase, override_settings
-
-class TestConsumer(TestCase):
-
- class DummyParser(object):
- pass
-
- def test__get_parser_class_1_parser(self):
- self.assertEqual(
- self._get_consumer()._get_parser_class("doc.pdf"),
- self.DummyParser
- )
-
- @mock.patch("documents.consumer.os.makedirs")
- @mock.patch("documents.consumer.os.path.exists", return_value=True)
- @mock.patch("documents.consumer.document_consumer_declaration.send")
- def test__get_parser_class_n_parsers(self, m, *args):
-
- class DummyParser1(object):
- pass
-
- class DummyParser2(object):
- pass
-
- m.return_value = (
- (None, lambda _: {"weight": 0, "parser": DummyParser1}),
- (None, lambda _: {"weight": 1, "parser": DummyParser2}),
- )
- with TemporaryDirectory() as tmpdir:
- self.assertEqual(
- Consumer(consume=tmpdir)._get_parser_class("doc.pdf"),
- DummyParser2
- )
-
- @mock.patch("documents.consumer.os.makedirs")
- @mock.patch("documents.consumer.os.path.exists", return_value=True)
- @mock.patch("documents.consumer.document_consumer_declaration.send")
- def test__get_parser_class_0_parsers(self, m, *args):
- m.return_value = ((None, lambda _: None),)
- with TemporaryDirectory() as tmpdir:
- self.assertIsNone(
- Consumer(consume=tmpdir)._get_parser_class("doc.pdf")
- )
-
- @mock.patch("documents.consumer.os.makedirs")
- @mock.patch("documents.consumer.os.path.exists", return_value=True)
- @mock.patch("documents.consumer.document_consumer_declaration.send")
- def _get_consumer(self, m, *args):
- m.return_value = (
- (None, lambda _: {"weight": 0, "parser": self.DummyParser}),
- )
- with TemporaryDirectory() as tmpdir:
- return Consumer(consume=tmpdir)
+from .utils import DirectoriesMixin
+from ..consumer import Consumer, ConsumerError
+from ..models import FileInfo, Tag, Correspondent, DocumentType, Document
+from ..parsers import DocumentParser, ParseError
class TestAttributes(TestCase):
TAGS = ("tag1", "tag2", "tag3")
- EXTENSIONS = (
- "pdf", "png", "jpg", "jpeg", "gif", "tiff", "tif",
- "PDF", "PNG", "JPG", "JPEG", "GIF", "TIFF", "TIF",
- "PdF", "PnG", "JpG", "JPeG", "GiF", "TiFf", "TiF",
- )
- def _test_guess_attributes_from_name(self, path, sender, title, tags):
+ def _test_guess_attributes_from_name(self, filename, sender, title, tags):
+ file_info = FileInfo.from_filename(filename)
- for extension in self.EXTENSIONS:
+ if sender:
+ self.assertEqual(file_info.correspondent.name, sender, filename)
+ else:
+ self.assertIsNone(file_info.correspondent, filename)
- f = path.format(extension)
- file_info = FileInfo.from_path(f)
+ self.assertEqual(file_info.title, title, filename)
- if sender:
- self.assertEqual(file_info.correspondent.name, sender, f)
- else:
- self.assertIsNone(file_info.correspondent, f)
-
- self.assertEqual(file_info.title, title, f)
-
- self.assertEqual(tuple([t.slug for t in file_info.tags]), tags, f)
- if extension.lower() == "jpeg":
- self.assertEqual(file_info.extension, "jpg", f)
- elif extension.lower() == "tif":
- self.assertEqual(file_info.extension, "tiff", f)
- else:
- self.assertEqual(file_info.extension, extension.lower(), f)
+ self.assertEqual(tuple([t.name for t in file_info.tags]), tags, filename)
def test_guess_attributes_from_name0(self):
self._test_guess_attributes_from_name(
- "/path/to/Sender - Title.{}", "Sender", "Title", ())
+ "Sender - Title.pdf", "Sender", "Title", ())
def test_guess_attributes_from_name1(self):
self._test_guess_attributes_from_name(
- "/path/to/Spaced Sender - Title.{}", "Spaced Sender", "Title", ())
+ "Spaced Sender - Title.pdf", "Spaced Sender", "Title", ())
def test_guess_attributes_from_name2(self):
self._test_guess_attributes_from_name(
- "/path/to/Sender - Spaced Title.{}", "Sender", "Spaced Title", ())
+ "Sender - Spaced Title.pdf", "Sender", "Spaced Title", ())
def test_guess_attributes_from_name3(self):
self._test_guess_attributes_from_name(
- "/path/to/Dashed-Sender - Title.{}", "Dashed-Sender", "Title", ())
+ "Dashed-Sender - Title.pdf", "Dashed-Sender", "Title", ())
def test_guess_attributes_from_name4(self):
self._test_guess_attributes_from_name(
- "/path/to/Sender - Dashed-Title.{}", "Sender", "Dashed-Title", ())
+ "Sender - Dashed-Title.pdf", "Sender", "Dashed-Title", ())
def test_guess_attributes_from_name5(self):
self._test_guess_attributes_from_name(
- "/path/to/Sender - Title - tag1,tag2,tag3.{}",
+ "Sender - Title - tag1,tag2,tag3.pdf",
"Sender",
"Title",
self.TAGS
@@ -122,7 +59,7 @@ class TestAttributes(TestCase):
def test_guess_attributes_from_name6(self):
self._test_guess_attributes_from_name(
- "/path/to/Spaced Sender - Title - tag1,tag2,tag3.{}",
+ "Spaced Sender - Title - tag1,tag2,tag3.pdf",
"Spaced Sender",
"Title",
self.TAGS
@@ -130,7 +67,7 @@ class TestAttributes(TestCase):
def test_guess_attributes_from_name7(self):
self._test_guess_attributes_from_name(
- "/path/to/Sender - Spaced Title - tag1,tag2,tag3.{}",
+ "Sender - Spaced Title - tag1,tag2,tag3.pdf",
"Sender",
"Spaced Title",
self.TAGS
@@ -138,7 +75,7 @@ class TestAttributes(TestCase):
def test_guess_attributes_from_name8(self):
self._test_guess_attributes_from_name(
- "/path/to/Dashed-Sender - Title - tag1,tag2,tag3.{}",
+ "Dashed-Sender - Title - tag1,tag2,tag3.pdf",
"Dashed-Sender",
"Title",
self.TAGS
@@ -146,7 +83,7 @@ class TestAttributes(TestCase):
def test_guess_attributes_from_name9(self):
self._test_guess_attributes_from_name(
- "/path/to/Sender - Dashed-Title - tag1,tag2,tag3.{}",
+ "Sender - Dashed-Title - tag1,tag2,tag3.pdf",
"Sender",
"Dashed-Title",
self.TAGS
@@ -154,7 +91,7 @@ class TestAttributes(TestCase):
def test_guess_attributes_from_name10(self):
self._test_guess_attributes_from_name(
- "/path/to/Σενδερ - Τιτλε - tag1,tag2,tag3.{}",
+ "Σενδερ - Τιτλε - tag1,tag2,tag3.pdf",
"Σενδερ",
"Τιτλε",
self.TAGS
@@ -162,7 +99,7 @@ class TestAttributes(TestCase):
def test_guess_attributes_from_name_when_correspondent_empty(self):
self._test_guess_attributes_from_name(
- '/path/to/ - weird empty correspondent but should not break.{}',
+ ' - weird empty correspondent but should not break.pdf',
None,
'weird empty correspondent but should not break',
()
@@ -170,7 +107,7 @@ class TestAttributes(TestCase):
def test_guess_attributes_from_name_when_title_starts_with_dash(self):
self._test_guess_attributes_from_name(
- '/path/to/- weird but should not break.{}',
+ '- weird but should not break.pdf',
None,
'- weird but should not break',
()
@@ -178,7 +115,7 @@ class TestAttributes(TestCase):
def test_guess_attributes_from_name_when_title_ends_with_dash(self):
self._test_guess_attributes_from_name(
- '/path/to/weird but should not break -.{}',
+ 'weird but should not break -.pdf',
None,
'weird but should not break -',
()
@@ -186,7 +123,7 @@ class TestAttributes(TestCase):
def test_guess_attributes_from_name_when_title_is_empty(self):
self._test_guess_attributes_from_name(
- '/path/to/weird correspondent but should not break - .{}',
+ 'weird correspondent but should not break - .pdf',
'weird correspondent but should not break',
'',
()
@@ -198,11 +135,11 @@ class TestAttributes(TestCase):
:return:
"""
- path = "Title - Correspondent - tAg1,TAG2.pdf"
- self.assertEqual(len(FileInfo.from_path(path).tags), 2)
+ filename = "Title - Correspondent - tAg1,TAG2.pdf"
+ self.assertEqual(len(FileInfo.from_filename(filename).tags), 2)
path = "Title - Correspondent - tag1,tag2.pdf"
- self.assertEqual(len(FileInfo.from_path(path).tags), 2)
+ self.assertEqual(len(FileInfo.from_filename(filename).tags), 2)
self.assertEqual(Tag.objects.all().count(), 2)
@@ -222,13 +159,12 @@ class TestFieldPermutations(TestCase):
]
valid_titles = ["title", "Title w Spaces", "Title a-dash", "Τίτλος", ""]
valid_tags = ["tag", "tig,tag", "tag1,tag2,tag-3"]
- valid_extensions = ["pdf", "png", "jpg", "jpeg", "gif"]
def _test_guessed_attributes(self, filename, created=None,
correspondent=None, title=None,
- extension=None, tags=None):
+ tags=None):
- info = FileInfo.from_path(filename)
+ info = FileInfo.from_filename(filename)
# Created
if created is None:
@@ -252,72 +188,60 @@ class TestFieldPermutations(TestCase):
self.assertEqual(info.tags, (), filename)
else:
self.assertEqual(
- [t.slug for t in info.tags], tags.split(','),
+ [t.name for t in info.tags], tags.split(','),
filename
)
- # Extension
- if extension == 'jpeg':
- extension = 'jpg'
- self.assertEqual(info.extension, extension, filename)
-
def test_just_title(self):
- template = '/path/to/{title}.{extension}'
+ template = '{title}.pdf'
for title in self.valid_titles:
- for extension in self.valid_extensions:
- spec = dict(title=title, extension=extension)
+ spec = dict(title=title)
+ filename = template.format(**spec)
+ self._test_guessed_attributes(filename, **spec)
+
+ def test_title_and_correspondent(self):
+ template = '{correspondent} - {title}.pdf'
+ for correspondent in self.valid_correspondents:
+ for title in self.valid_titles:
+ spec = dict(correspondent=correspondent, title=title)
filename = template.format(**spec)
self._test_guessed_attributes(filename, **spec)
- def test_title_and_correspondent(self):
- template = '/path/to/{correspondent} - {title}.{extension}'
- for correspondent in self.valid_correspondents:
- for title in self.valid_titles:
- for extension in self.valid_extensions:
- spec = dict(correspondent=correspondent, title=title,
- extension=extension)
- filename = template.format(**spec)
- self._test_guessed_attributes(filename, **spec)
-
def test_title_and_correspondent_and_tags(self):
- template = '/path/to/{correspondent} - {title} - {tags}.{extension}'
+ template = '{correspondent} - {title} - {tags}.pdf'
for correspondent in self.valid_correspondents:
for title in self.valid_titles:
for tags in self.valid_tags:
- for extension in self.valid_extensions:
- spec = dict(correspondent=correspondent, title=title,
- tags=tags, extension=extension)
- filename = template.format(**spec)
- self._test_guessed_attributes(filename, **spec)
+ spec = dict(correspondent=correspondent, title=title,
+ tags=tags)
+ filename = template.format(**spec)
+ self._test_guessed_attributes(filename, **spec)
def test_created_and_correspondent_and_title_and_tags(self):
template = (
- "/path/to/{created} - "
+ "{created} - "
"{correspondent} - "
"{title} - "
- "{tags}"
- ".{extension}"
+ "{tags}.pdf"
)
for created in self.valid_dates:
for correspondent in self.valid_correspondents:
for title in self.valid_titles:
for tags in self.valid_tags:
- for extension in self.valid_extensions:
- spec = {
- "created": created,
- "correspondent": correspondent,
- "title": title,
- "tags": tags,
- "extension": extension
- }
- self._test_guessed_attributes(
- template.format(**spec), **spec)
+ spec = {
+ "created": created,
+ "correspondent": correspondent,
+ "title": title,
+ "tags": tags,
+ }
+ self._test_guessed_attributes(
+ template.format(**spec), **spec)
def test_created_and_correspondent_and_title(self):
- template = "/path/to/{created} - {correspondent} - {title}.{extension}"
+ template = "{created} - {correspondent} - {title}.pdf"
for created in self.valid_dates:
for correspondent in self.valid_correspondents:
@@ -328,56 +252,50 @@ class TestFieldPermutations(TestCase):
if title.lower() == title:
continue
- for extension in self.valid_extensions:
- spec = {
- "created": created,
- "correspondent": correspondent,
- "title": title,
- "extension": extension
- }
- self._test_guessed_attributes(
- template.format(**spec), **spec)
-
- def test_created_and_title(self):
-
- template = "/path/to/{created} - {title}.{extension}"
-
- for created in self.valid_dates:
- for title in self.valid_titles:
- for extension in self.valid_extensions:
spec = {
"created": created,
- "title": title,
- "extension": extension
+ "correspondent": correspondent,
+ "title": title
}
self._test_guessed_attributes(
template.format(**spec), **spec)
+ def test_created_and_title(self):
+
+ template = "{created} - {title}.pdf"
+
+ for created in self.valid_dates:
+ for title in self.valid_titles:
+ spec = {
+ "created": created,
+ "title": title
+ }
+ self._test_guessed_attributes(
+ template.format(**spec), **spec)
+
def test_created_and_title_and_tags(self):
- template = "/path/to/{created} - {title} - {tags}.{extension}"
+ template = "{created} - {title} - {tags}.pdf"
for created in self.valid_dates:
for title in self.valid_titles:
for tags in self.valid_tags:
- for extension in self.valid_extensions:
- spec = {
- "created": created,
- "title": title,
- "tags": tags,
- "extension": extension
- }
- self._test_guessed_attributes(
- template.format(**spec), **spec)
+ spec = {
+ "created": created,
+ "title": title,
+ "tags": tags
+ }
+ self._test_guessed_attributes(
+ template.format(**spec), **spec)
def test_invalid_date_format(self):
- info = FileInfo.from_path("/path/to/06112017Z - title.pdf")
+ info = FileInfo.from_filename("06112017Z - title.pdf")
self.assertEqual(info.title, "title")
self.assertIsNone(info.created)
def test_filename_parse_transforms(self):
- path = "/some/path/to/tag1,tag2_20190908_180610_0001.pdf"
+ filename = "tag1,tag2_20190908_180610_0001.pdf"
all_patt = re.compile("^.*$")
none_patt = re.compile("$a")
exact_patt = re.compile("^([a-z0-9,]+)_(\\d{8})_(\\d{6})_([0-9]+)\\.")
@@ -385,53 +303,47 @@ class TestFieldPermutations(TestCase):
repl2 = "\\2Z - " + repl1 # creation date + repl1
# No transformations configured (= default)
- info = FileInfo.from_path(path)
+ info = FileInfo.from_filename(filename)
self.assertEqual(info.title, "tag1,tag2_20190908_180610_0001")
- self.assertEqual(info.extension, "pdf")
self.assertEqual(info.tags, ())
self.assertIsNone(info.created)
# Pattern doesn't match (filename unaltered)
with self.settings(
FILENAME_PARSE_TRANSFORMS=[(none_patt, "none.gif")]):
- info = FileInfo.from_path(path)
+ info = FileInfo.from_filename(filename)
self.assertEqual(info.title, "tag1,tag2_20190908_180610_0001")
- self.assertEqual(info.extension, "pdf")
# Simple transformation (match all)
with self.settings(
FILENAME_PARSE_TRANSFORMS=[(all_patt, "all.gif")]):
- info = FileInfo.from_path(path)
+ info = FileInfo.from_filename(filename)
self.assertEqual(info.title, "all")
- self.assertEqual(info.extension, "gif")
# Multiple transformations configured (first pattern matches)
with self.settings(
FILENAME_PARSE_TRANSFORMS=[
(all_patt, "all.gif"),
(all_patt, "anotherall.gif")]):
- info = FileInfo.from_path(path)
+ info = FileInfo.from_filename(filename)
self.assertEqual(info.title, "all")
- self.assertEqual(info.extension, "gif")
# Multiple transformations configured (second pattern matches)
with self.settings(
FILENAME_PARSE_TRANSFORMS=[
(none_patt, "none.gif"),
(all_patt, "anotherall.gif")]):
- info = FileInfo.from_path(path)
+ info = FileInfo.from_filename(filename)
self.assertEqual(info.title, "anotherall")
- self.assertEqual(info.extension, "gif")
# Complex transformation without date in replacement string
with self.settings(
FILENAME_PARSE_TRANSFORMS=[(exact_patt, repl1)]):
- info = FileInfo.from_path(path)
+ info = FileInfo.from_filename(filename)
self.assertEqual(info.title, "0001")
- self.assertEqual(info.extension, "pdf")
self.assertEqual(len(info.tags), 2)
- self.assertEqual(info.tags[0].slug, "tag1")
- self.assertEqual(info.tags[1].slug, "tag2")
+ self.assertEqual(info.tags[0].name, "tag1")
+ self.assertEqual(info.tags[1].name, "tag2")
self.assertIsNone(info.created)
# Complex transformation with date in replacement string
@@ -441,12 +353,292 @@ class TestFieldPermutations(TestCase):
(exact_patt, repl2), # <-- matches
(exact_patt, repl1),
(all_patt, "all.gif")]):
- info = FileInfo.from_path(path)
+ info = FileInfo.from_filename(filename)
self.assertEqual(info.title, "0001")
- self.assertEqual(info.extension, "pdf")
self.assertEqual(len(info.tags), 2)
- self.assertEqual(info.tags[0].slug, "tag1")
- self.assertEqual(info.tags[1].slug, "tag2")
+ self.assertEqual(info.tags[0].name, "tag1")
+ self.assertEqual(info.tags[1].name, "tag2")
self.assertEqual(info.created.year, 2019)
self.assertEqual(info.created.month, 9)
self.assertEqual(info.created.day, 8)
+
+
+class DummyParser(DocumentParser):
+
+ def get_thumbnail(self, document_path, mime_type):
+ # not important during tests
+ raise NotImplementedError()
+
+ def __init__(self, logging_group, scratch_dir, archive_path):
+ super(DummyParser, self).__init__(logging_group)
+ _, self.fake_thumb = tempfile.mkstemp(suffix=".png", dir=scratch_dir)
+ self.archive_path = archive_path
+
+ def get_optimised_thumbnail(self, document_path, mime_type):
+ return self.fake_thumb
+
+ def parse(self, document_path, mime_type):
+ self.text = "The Text"
+
+
+class FaultyParser(DocumentParser):
+
+ def get_thumbnail(self, document_path, mime_type):
+ # not important during tests
+ raise NotImplementedError()
+
+ def __init__(self, logging_group, scratch_dir):
+ super(FaultyParser, self).__init__(logging_group)
+ _, self.fake_thumb = tempfile.mkstemp(suffix=".png", dir=scratch_dir)
+
+ def get_optimised_thumbnail(self, document_path, mime_type):
+ return self.fake_thumb
+
+ def parse(self, document_path, mime_type):
+ raise ParseError("Does not compute.")
+
+
+def fake_magic_from_file(file, mime=False):
+
+ if mime:
+ if os.path.splitext(file)[1] == ".pdf":
+ return "application/pdf"
+ else:
+ return "unknown"
+ else:
+ return "A verbose string that describes the contents of the file"
+
+
+@mock.patch("documents.consumer.magic.from_file", fake_magic_from_file)
+class TestConsumer(DirectoriesMixin, TestCase):
+
+ def make_dummy_parser(self, logging_group):
+ return DummyParser(logging_group, self.dirs.scratch_dir, self.get_test_archive_file())
+
+ def make_faulty_parser(self, logging_group):
+ return FaultyParser(logging_group, self.dirs.scratch_dir)
+
+ def setUp(self):
+ super(TestConsumer, self).setUp()
+
+ patcher = mock.patch("documents.parsers.document_consumer_declaration.send")
+ m = patcher.start()
+ m.return_value = [(None, {
+ "parser": self.make_dummy_parser,
+ "mime_types": {"application/pdf": ".pdf"},
+ "weight": 0
+ })]
+
+ self.addCleanup(patcher.stop)
+
+ self.consumer = Consumer()
+
+ def get_test_file(self):
+ src = os.path.join(os.path.dirname(__file__), "samples", "documents", "originals", "0000001.pdf")
+ dst = os.path.join(self.dirs.scratch_dir, "sample.pdf")
+ shutil.copy(src, dst)
+ return dst
+
+ def get_test_archive_file(self):
+ src = os.path.join(os.path.dirname(__file__), "samples", "documents", "archive", "0000001.pdf")
+ dst = os.path.join(self.dirs.scratch_dir, "sample_archive.pdf")
+ shutil.copy(src, dst)
+ return dst
+
+ @override_settings(PAPERLESS_FILENAME_FORMAT=None)
+ def testNormalOperation(self):
+
+ filename = self.get_test_file()
+ document = self.consumer.try_consume_file(filename)
+
+ self.assertEqual(document.content, "The Text")
+ self.assertEqual(document.title, os.path.splitext(os.path.basename(filename))[0])
+ self.assertIsNone(document.correspondent)
+ self.assertIsNone(document.document_type)
+ self.assertEqual(document.filename, "0000001.pdf")
+
+ self.assertTrue(os.path.isfile(
+ document.source_path
+ ))
+
+ self.assertTrue(os.path.isfile(
+ document.thumbnail_path
+ ))
+
+ self.assertTrue(os.path.isfile(
+ document.archive_path
+ ))
+
+ self.assertEqual(document.checksum, "42995833e01aea9b3edee44bbfdd7ce1")
+ self.assertEqual(document.archive_checksum, "62acb0bcbfbcaa62ca6ad3668e4e404b")
+
+ self.assertFalse(os.path.isfile(filename))
+
+ def testOverrideFilename(self):
+ filename = self.get_test_file()
+ override_filename = "My Bank - Statement for November.pdf"
+
+ document = self.consumer.try_consume_file(filename, override_filename=override_filename)
+
+ self.assertEqual(document.correspondent.name, "My Bank")
+ self.assertEqual(document.title, "Statement for November")
+
+ def testOverrideTitle(self):
+
+ document = self.consumer.try_consume_file(self.get_test_file(), override_title="Override Title")
+ self.assertEqual(document.title, "Override Title")
+
+ def testOverrideCorrespondent(self):
+ c = Correspondent.objects.create(name="test")
+
+ document = self.consumer.try_consume_file(self.get_test_file(), override_correspondent_id=c.pk)
+ self.assertEqual(document.correspondent.id, c.id)
+
+ def testOverrideDocumentType(self):
+ dt = DocumentType.objects.create(name="test")
+
+ document = self.consumer.try_consume_file(self.get_test_file(), override_document_type_id=dt.pk)
+ self.assertEqual(document.document_type.id, dt.id)
+
+ def testOverrideTags(self):
+ t1 = Tag.objects.create(name="t1")
+ t2 = Tag.objects.create(name="t2")
+ t3 = Tag.objects.create(name="t3")
+ document = self.consumer.try_consume_file(self.get_test_file(), override_tag_ids=[t1.id, t3.id])
+
+ self.assertIn(t1, document.tags.all())
+ self.assertNotIn(t2, document.tags.all())
+ self.assertIn(t3, document.tags.all())
+
+ def testNotAFile(self):
+ try:
+ self.consumer.try_consume_file("non-existing-file")
+ except ConsumerError as e:
+ self.assertTrue(str(e).endswith('It is not a file'))
+ return
+
+ self.fail("Should throw exception")
+
+ def testDuplicates1(self):
+ self.consumer.try_consume_file(self.get_test_file())
+
+ try:
+ self.consumer.try_consume_file(self.get_test_file())
+ except ConsumerError as e:
+ self.assertTrue(str(e).endswith("It is a duplicate."))
+ return
+
+ self.fail("Should throw exception")
+
+ def testDuplicates2(self):
+ self.consumer.try_consume_file(self.get_test_file())
+
+ try:
+ self.consumer.try_consume_file(self.get_test_archive_file())
+ except ConsumerError as e:
+ self.assertTrue(str(e).endswith("It is a duplicate."))
+ return
+
+ self.fail("Should throw exception")
+
+ def testDuplicates3(self):
+ self.consumer.try_consume_file(self.get_test_archive_file())
+ self.consumer.try_consume_file(self.get_test_file())
+
+ @mock.patch("documents.parsers.document_consumer_declaration.send")
+ def testNoParsers(self, m):
+ m.return_value = []
+
+ try:
+ self.consumer.try_consume_file(self.get_test_file())
+ except ConsumerError as e:
+ self.assertTrue("No parsers abvailable for" in str(e))
+ return
+
+ self.fail("Should throw exception")
+
+ @mock.patch("documents.parsers.document_consumer_declaration.send")
+ def testFaultyParser(self, m):
+ m.return_value = [(None, {
+ "parser": self.make_faulty_parser,
+ "mime_types": {"application/pdf": ".pdf"},
+ "weight": 0
+ })]
+
+ try:
+ self.consumer.try_consume_file(self.get_test_file())
+ except ConsumerError as e:
+ self.assertEqual(str(e), "Does not compute.")
+ return
+
+ self.fail("Should throw exception.")
+
+ @mock.patch("documents.consumer.Consumer._write")
+ def testPostSaveError(self, m):
+ filename = self.get_test_file()
+ m.side_effect = OSError("NO.")
+ try:
+ self.consumer.try_consume_file(filename)
+ except ConsumerError as e:
+ self.assertEqual(str(e), "NO.")
+ else:
+ self.fail("Should raise exception")
+
+ # file not deleted
+ self.assertTrue(os.path.isfile(filename))
+
+ # Database empty
+ self.assertEqual(len(Document.objects.all()), 0)
+
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/{title}")
+ def testFilenameHandling(self):
+ filename = self.get_test_file()
+
+ document = self.consumer.try_consume_file(filename, override_filename="Bank - Test.pdf", override_title="new docs")
+
+ self.assertEqual(document.title, "new docs")
+ self.assertEqual(document.correspondent.name, "Bank")
+ self.assertEqual(document.filename, "Bank/new docs.pdf")
+
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/{title}")
+ @mock.patch("documents.signals.handlers.generate_unique_filename")
+ def testFilenameHandlingUnstableFormat(self, m):
+
+ filenames = ["this", "that", "now this", "i cant decide"]
+
+ def get_filename():
+ f = filenames.pop()
+ filenames.insert(0, f)
+ return f
+
+ m.side_effect = lambda f, root: get_filename()
+
+ filename = self.get_test_file()
+
+ Tag.objects.create(name="test", is_inbox_tag=True)
+
+ document = self.consumer.try_consume_file(filename, override_filename="Bank - Test.pdf", override_title="new docs")
+
+ self.assertEqual(document.title, "new docs")
+ self.assertEqual(document.correspondent.name, "Bank")
+ self.assertIsNotNone(os.path.isfile(document.title))
+ self.assertTrue(os.path.isfile(document.source_path))
+
+ @mock.patch("documents.consumer.DocumentClassifier")
+ def testClassifyDocument(self, m):
+ correspondent = Correspondent.objects.create(name="test")
+ dtype = DocumentType.objects.create(name="test")
+ t1 = Tag.objects.create(name="t1")
+ t2 = Tag.objects.create(name="t2")
+
+ m.return_value = MagicMock()
+ m.return_value.predict_correspondent.return_value = correspondent.pk
+ m.return_value.predict_document_type.return_value = dtype.pk
+ m.return_value.predict_tags.return_value = [t1.pk]
+
+ document = self.consumer.try_consume_file(self.get_test_file())
+
+ self.assertEqual(document.correspondent, correspondent)
+ self.assertEqual(document.document_type, dtype)
+ self.assertIn(t1, document.tags.all())
+ self.assertNotIn(t2, document.tags.all())
diff --git a/src/documents/tests/test_date_parsing.py b/src/documents/tests/test_date_parsing.py
new file mode 100644
index 000000000..357b0937e
--- /dev/null
+++ b/src/documents/tests/test_date_parsing.py
@@ -0,0 +1,140 @@
+import datetime
+import os
+import shutil
+from unittest import mock
+from uuid import uuid4
+
+from dateutil import tz
+from django.conf import settings
+from django.test import TestCase, override_settings
+
+from documents.parsers import parse_date
+from paperless_tesseract.parsers import RasterisedDocumentParser
+
+
+class TestDate(TestCase):
+
+ SAMPLE_FILES = os.path.join(os.path.dirname(__file__), "../../paperless_tesseract/tests/samples")
+ SCRATCH = "/tmp/paperless-tests-{}".format(str(uuid4())[:8])
+
+ def setUp(self):
+ os.makedirs(self.SCRATCH, exist_ok=True)
+
+ def tearDown(self):
+ shutil.rmtree(self.SCRATCH)
+
+ def test_date_format_1(self):
+ text = "lorem ipsum 130218 lorem ipsum"
+ self.assertEqual(parse_date("", text), None)
+
+ def test_date_format_2(self):
+ text = "lorem ipsum 2018 lorem ipsum"
+ self.assertEqual(parse_date("", text), None)
+
+ def test_date_format_3(self):
+ text = "lorem ipsum 20180213 lorem ipsum"
+ self.assertEqual(parse_date("", text), None)
+
+ def test_date_format_4(self):
+ text = "lorem ipsum 13.02.2018 lorem ipsum"
+ date = parse_date("", text)
+ self.assertEqual(
+ date,
+ datetime.datetime(
+ 2018, 2, 13, 0, 0,
+ tzinfo=tz.gettz(settings.TIME_ZONE)
+ )
+ )
+
+ def test_date_format_5(self):
+ text = (
+ "lorem ipsum 130218, 2018, 20180213 and lorem 13.02.2018 lorem "
+ "ipsum"
+ )
+ date = parse_date("", text)
+ self.assertEqual(
+ date,
+ datetime.datetime(
+ 2018, 2, 13, 0, 0,
+ tzinfo=tz.gettz(settings.TIME_ZONE)
+ )
+ )
+
+ def test_date_format_6(self):
+ text = (
+ "lorem ipsum\n"
+ "Wohnort\n"
+ "3100\n"
+ "IBAN\n"
+ "AT87 4534\n"
+ "1234\n"
+ "1234 5678\n"
+ "BIC\n"
+ "lorem ipsum"
+ )
+ self.assertEqual(parse_date("", text), None)
+
+ def test_date_format_7(self):
+ text = (
+ "lorem ipsum\n"
+ "März 2019\n"
+ "lorem ipsum"
+ )
+ date = parse_date("", text)
+ self.assertEqual(
+ date,
+ datetime.datetime(
+ 2019, 3, 1, 0, 0,
+ tzinfo=tz.gettz(settings.TIME_ZONE)
+ )
+ )
+
+ def test_date_format_8(self):
+ text = (
+ "lorem ipsum\n"
+ "Wohnort\n"
+ "3100\n"
+ "IBAN\n"
+ "AT87 4534\n"
+ "1234\n"
+ "1234 5678\n"
+ "BIC\n"
+ "lorem ipsum\n"
+ "März 2020"
+ )
+ self.assertEqual(
+ parse_date("", text),
+ datetime.datetime(
+ 2020, 3, 1, 0, 0,
+ tzinfo=tz.gettz(settings.TIME_ZONE)
+ )
+ )
+
+ @override_settings(SCRATCH_DIR=SCRATCH)
+ def test_date_format_9(self):
+ text = (
+ "lorem ipsum\n"
+ "27. Nullmonth 2020\n"
+ "März 2020\n"
+ "lorem ipsum"
+ )
+ self.assertEqual(
+ parse_date("", text),
+ datetime.datetime(
+ 2020, 3, 1, 0, 0,
+ tzinfo=tz.gettz(settings.TIME_ZONE)
+ )
+ )
+
+ def test_crazy_date_past(self, *args):
+ self.assertIsNone(parse_date("", "01-07-0590 00:00:00"))
+
+ def test_crazy_date_future(self, *args):
+ self.assertIsNone(parse_date("", "01-07-2350 00:00:00"))
+
+ def test_crazy_date_with_spaces(self, *args):
+ self.assertIsNone(parse_date("", "20 408000l 2475"))
+
+ @override_settings(FILENAME_DATE_ORDER="YMD")
+ def test_filename_date_parse_invalid(self, *args):
+ self.assertIsNone(parse_date("/tmp/20 408000l 2475 - test.pdf", "No date in here"))
diff --git a/src/documents/tests/test_document_model.py b/src/documents/tests/test_document_model.py
index 2da674527..74bd9a2a7 100644
--- a/src/documents/tests/test_document_model.py
+++ b/src/documents/tests/test_document_model.py
@@ -1,23 +1,66 @@
+import shutil
+import tempfile
+from datetime import datetime
+from pathlib import Path
from unittest import mock
-from django.test import TestCase
+from django.test import TestCase, override_settings
from ..models import Document, Correspondent
class TestDocument(TestCase):
+ def setUp(self) -> None:
+ self.originals_dir = tempfile.mkdtemp()
+ self.thumb_dir = tempfile.mkdtemp()
+
+ override_settings(
+ ORIGINALS_DIR=self.originals_dir,
+ THUMBNAIL_DIR=self.thumb_dir,
+ ).enable()
+
+ def tearDown(self) -> None:
+ shutil.rmtree(self.originals_dir)
+ shutil.rmtree(self.thumb_dir)
+
def test_file_deletion(self):
document = Document.objects.create(
correspondent=Correspondent.objects.create(name="Test0"),
title="Title",
content="content",
checksum="checksum",
+ mime_type="application/pdf"
)
+
file_path = document.source_path
thumb_path = document.thumbnail_path
+
+ Path(file_path).touch()
+ Path(thumb_path).touch()
+
with mock.patch("documents.signals.handlers.os.unlink") as mock_unlink:
document.delete()
mock_unlink.assert_any_call(file_path)
mock_unlink.assert_any_call(thumb_path)
self.assertEqual(mock_unlink.call_count, 2)
+
+ def test_file_name(self):
+
+ doc = Document(mime_type="application/pdf", title="test", created=datetime(2020, 12, 25))
+ self.assertEqual(doc.get_public_filename(), "2020-12-25 test.pdf")
+
+ def test_file_name_jpg(self):
+
+ doc = Document(mime_type="image/jpeg", title="test", created=datetime(2020, 12, 25))
+ self.assertEqual(doc.get_public_filename(), "2020-12-25 test.jpg")
+
+ def test_file_name_unknown(self):
+
+ doc = Document(mime_type="application/zip", title="test", created=datetime(2020, 12, 25))
+ self.assertEqual(doc.get_public_filename(), "2020-12-25 test.zip")
+
+ def test_file_name_invalid_type(self):
+
+ doc = Document(mime_type="image/jpegasd", title="test", created=datetime(2020, 12, 25))
+ self.assertEqual(doc.get_public_filename(), "2020-12-25 test")
diff --git a/src/documents/tests/test_file_handling.py b/src/documents/tests/test_file_handling.py
index d55a50cd2..2e60065f1 100644
--- a/src/documents/tests/test_file_handling.py
+++ b/src/documents/tests/test_file_handling.py
@@ -1,208 +1,199 @@
import datetime
+import hashlib
import os
-import shutil
-from unittest import mock
-from uuid import uuid4
+import random
+import uuid
from pathlib import Path
-from shutil import rmtree
+from unittest import mock
-from dateutil import tz
-from django.test import TestCase, override_settings
-
-from django.utils.text import slugify
-from ..models import Tag, Document, Correspondent
from django.conf import settings
+from django.db import DatabaseError
+from django.test import TestCase, override_settings
+from django.utils import timezone
+
+from .utils import DirectoriesMixin
+from ..file_handling import generate_filename, create_source_path_directory, delete_empty_directories, \
+ generate_unique_filename
+from ..models import Document, Correspondent, Tag
-class TestDate(TestCase):
- deletion_list = []
-
- def add_to_deletion_list(self, dirname):
- self.deletion_list.append(dirname)
-
- def setUp(self):
- folder = "/tmp/paperless-tests-{}".format(str(uuid4())[:8])
- os.makedirs(folder + "/documents/originals")
- storage_override = override_settings(MEDIA_ROOT=folder)
- storage_override.enable()
- self.add_to_deletion_list(folder)
-
- def tearDown(self):
- for dirname in self.deletion_list:
- shutil.rmtree(dirname, ignore_errors=True)
-
- @override_settings(PAPERLESS_FILENAME_FORMAT="")
- def test_source_filename(self):
- document = Document()
- document.file_type = "pdf"
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
- document.save()
-
- self.assertEqual(document.source_filename, "0000001.pdf")
-
- document.filename = "test.pdf"
- self.assertEqual(document.source_filename, "test.pdf")
+class TestFileHandling(DirectoriesMixin, TestCase):
@override_settings(PAPERLESS_FILENAME_FORMAT="")
def test_generate_source_filename(self):
document = Document()
- document.file_type = "pdf"
+ document.mime_type = "application/pdf"
document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
document.save()
- self.assertEqual(document.generate_source_filename(), "0000001.pdf")
+ self.assertEqual(generate_filename(document), "{:07d}.pdf".format(document.pk))
document.storage_type = Document.STORAGE_TYPE_GPG
- self.assertEqual(document.generate_source_filename(),
- "0000001.pdf.gpg")
+ self.assertEqual(generate_filename(document),
+ "{:07d}.pdf.gpg".format(document.pk))
- @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/" +
- "{correspondent}")
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/{correspondent}")
def test_file_renaming(self):
document = Document()
- document.file_type = "pdf"
+ document.mime_type = "application/pdf"
document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
document.save()
- # Ensure that filename is properly generated
- tmp = document.source_filename
- self.assertEqual(document.generate_source_filename(),
- "none/none-0000001.pdf")
- document.create_source_directory()
- Path(document.source_path).touch()
+ # Test default source_path
+ self.assertEqual(document.source_path, settings.ORIGINALS_DIR + "/{:07d}.pdf".format(document.pk))
- # Test source_path
- self.assertEqual(document.source_path, settings.MEDIA_ROOT +
- "/documents/originals/none/none-0000001.pdf")
+ document.filename = generate_filename(document)
+
+ # Ensure that filename is properly generated
+ self.assertEqual(document.filename, "none/none.pdf")
# Enable encryption and check again
document.storage_type = Document.STORAGE_TYPE_GPG
- tmp = document.source_filename
- self.assertEqual(document.generate_source_filename(),
- "none/none-0000001.pdf.gpg")
+ document.filename = generate_filename(document)
+ self.assertEqual(document.filename,
+ "none/none.pdf.gpg")
+
document.save()
- self.assertEqual(os.path.isdir(settings.MEDIA_ROOT +
- "/documents/originals/none"), True)
+ # test that creating dirs for the source_path creates the correct directory
+ create_source_path_directory(document.source_path)
+ Path(document.source_path).touch()
+ self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR + "/none"), True)
# Set a correspondent and save the document
- document.correspondent = Correspondent.objects.get_or_create(
- name="test")[0]
+ document.correspondent = Correspondent.objects.get_or_create(name="test")[0]
document.save()
# Check proper handling of files
- self.assertEqual(os.path.isdir(settings.MEDIA_ROOT +
- "/documents/originals/test"), True)
- self.assertEqual(os.path.isdir(settings.MEDIA_ROOT +
- "/documents/originals/none"), False)
- self.assertEqual(os.path.isfile(settings.MEDIA_ROOT + "/documents/" +
- "originals/test/test-0000001.pdf.gpg"), True)
- self.assertEqual(document.generate_source_filename(),
- "test/test-0000001.pdf.gpg")
+ self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR + "/test"), True)
+ self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR + "/none"), False)
+ self.assertEqual(os.path.isfile(settings.ORIGINALS_DIR + "/test/test.pdf.gpg"), True)
- @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/" +
- "{correspondent}")
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/{correspondent}")
def test_file_renaming_missing_permissions(self):
document = Document()
- document.file_type = "pdf"
+ document.mime_type = "application/pdf"
document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
document.save()
# Ensure that filename is properly generated
- tmp = document.source_filename
- self.assertEqual(document.generate_source_filename(),
- "none/none-0000001.pdf")
- document.create_source_directory()
+ document.filename = generate_filename(document)
+ self.assertEqual(document.filename,
+ "none/none.pdf")
+ create_source_path_directory(document.source_path)
Path(document.source_path).touch()
# Test source_path
- self.assertEqual(document.source_path, settings.MEDIA_ROOT +
- "/documents/originals/none/none-0000001.pdf")
+ self.assertEqual(document.source_path, settings.ORIGINALS_DIR + "/none/none.pdf")
# Make the folder read- and execute-only (no writing and no renaming)
- os.chmod(settings.MEDIA_ROOT + "/documents/originals/none", 0o555)
+ os.chmod(settings.ORIGINALS_DIR + "/none", 0o555)
# Set a correspondent and save the document
- document.correspondent = Correspondent.objects.get_or_create(
- name="test")[0]
+ document.correspondent = Correspondent.objects.get_or_create(name="test")[0]
document.save()
# Check proper handling of files
- self.assertEqual(os.path.isfile(settings.MEDIA_ROOT + "/documents/" +
- "originals/none/none-0000001.pdf"), True)
- self.assertEqual(document.source_filename,
- "none/none-0000001.pdf")
+ self.assertEqual(os.path.isfile(settings.ORIGINALS_DIR + "/none/none.pdf"), True)
+ self.assertEqual(document.filename, "none/none.pdf")
- os.chmod(settings.MEDIA_ROOT + "/documents/originals/none", 0o777)
+ os.chmod(settings.ORIGINALS_DIR + "/none", 0o777)
+
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/{correspondent}")
+ def test_file_renaming_database_error(self):
+
+ document1 = Document.objects.create(mime_type="application/pdf", storage_type=Document.STORAGE_TYPE_UNENCRYPTED, checksum="AAAAA")
- @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/" +
- "{correspondent}")
- def test_document_delete(self):
document = Document()
- document.file_type = "pdf"
+ document.mime_type = "application/pdf"
+ document.checksum = "BBBBB"
document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
document.save()
# Ensure that filename is properly generated
- tmp = document.source_filename
- self.assertEqual(document.generate_source_filename(),
- "none/none-0000001.pdf")
- document.create_source_directory()
+ document.filename = generate_filename(document)
+ self.assertEqual(document.filename,
+ "none/none.pdf")
+ create_source_path_directory(document.source_path)
+ Path(document.source_path).touch()
+
+ # Test source_path
+ self.assertTrue(os.path.isfile(document.source_path))
+
+ # Set a correspondent and save the document
+ document.correspondent = Correspondent.objects.get_or_create(
+ name="test")[0]
+
+ with mock.patch("documents.signals.handlers.Document.objects.filter") as m:
+ m.side_effect = DatabaseError()
+ document.save()
+
+ # Check proper handling of files
+ self.assertTrue(os.path.isfile(document.source_path))
+ self.assertEqual(os.path.isfile(settings.ORIGINALS_DIR + "/none/none.pdf"), True)
+ self.assertEqual(document.filename, "none/none.pdf")
+
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/{correspondent}")
+ def test_document_delete(self):
+ document = Document()
+ document.mime_type = "application/pdf"
+ document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
+ document.save()
+
+ # Ensure that filename is properly generated
+ document.filename = generate_filename(document)
+ self.assertEqual(document.filename,
+ "none/none.pdf")
+
+ create_source_path_directory(document.source_path)
Path(document.source_path).touch()
# Ensure file deletion after delete
+ pk = document.pk
document.delete()
- self.assertEqual(os.path.isfile(settings.MEDIA_ROOT +
- "/documents/originals/none/none-0000001.pdf"), False)
- self.assertEqual(os.path.isdir(settings.MEDIA_ROOT +
- "/documents/originals/none"), False)
+ self.assertEqual(os.path.isfile(settings.ORIGINALS_DIR + "/none/none.pdf"), False)
+ self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR + "/none"), False)
- @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/" +
- "{correspondent}")
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/{correspondent}")
def test_document_delete_nofile(self):
document = Document()
- document.file_type = "pdf"
+ document.mime_type = "application/pdf"
document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
document.save()
document.delete()
- @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/" +
- "{correspondent}")
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/{correspondent}")
def test_directory_not_empty(self):
document = Document()
- document.file_type = "pdf"
+ document.mime_type = "application/pdf"
document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
document.save()
# Ensure that filename is properly generated
- tmp = document.source_filename
- self.assertEqual(document.generate_source_filename(),
- "none/none-0000001.pdf")
- document.create_source_directory()
+ document.filename = generate_filename(document)
+ self.assertEqual(document.filename,
+ "none/none.pdf")
+
+ create_source_path_directory(document.source_path)
+
Path(document.source_path).touch()
- Path(document.source_path + "test").touch()
+ important_file = document.source_path + "test"
+ Path(important_file).touch()
# Set a correspondent and save the document
- document.correspondent = Correspondent.objects.get_or_create(
- name="test")[0]
+ document.correspondent = Correspondent.objects.get_or_create(name="test")[0]
document.save()
# Check proper handling of files
- self.assertEqual(os.path.isdir(settings.MEDIA_ROOT +
- "/documents/originals/test"), True)
- self.assertEqual(os.path.isdir(settings.MEDIA_ROOT +
- "/documents/originals/none"), True)
-
- # Cleanup
- os.remove(settings.MEDIA_ROOT +
- "/documents/originals/none/none-0000001.pdftest")
- os.rmdir(settings.MEDIA_ROOT + "/documents/originals/none")
+ self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR + "/test"), True)
+ self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR + "/none"), True)
+ self.assertTrue(os.path.isfile(important_file))
@override_settings(PAPERLESS_FILENAME_FORMAT="{tags[type]}")
def test_tags_with_underscore(self):
document = Document()
- document.file_type = "pdf"
+ document.mime_type = "application/pdf"
document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
document.save()
@@ -212,18 +203,13 @@ class TestDate(TestCase):
document.save()
# Ensure that filename is properly generated
- tmp = document.source_filename
- self.assertEqual(document.generate_source_filename(),
- "demo-0000001.pdf")
- document.create_source_directory()
- Path(document.source_path).touch()
-
- document.delete()
+ self.assertEqual(generate_filename(document),
+ "demo.pdf")
@override_settings(PAPERLESS_FILENAME_FORMAT="{tags[type]}")
def test_tags_with_dash(self):
document = Document()
- document.file_type = "pdf"
+ document.mime_type = "application/pdf"
document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
document.save()
@@ -233,18 +219,13 @@ class TestDate(TestCase):
document.save()
# Ensure that filename is properly generated
- tmp = document.source_filename
- self.assertEqual(document.generate_source_filename(),
- "demo-0000001.pdf")
- document.create_source_directory()
- Path(document.source_path).touch()
-
- document.delete()
+ self.assertEqual(generate_filename(document),
+ "demo.pdf")
@override_settings(PAPERLESS_FILENAME_FORMAT="{tags[type]}")
def test_tags_malformed(self):
document = Document()
- document.file_type = "pdf"
+ document.mime_type = "application/pdf"
document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
document.save()
@@ -254,18 +235,13 @@ class TestDate(TestCase):
document.save()
# Ensure that filename is properly generated
- tmp = document.source_filename
- self.assertEqual(document.generate_source_filename(),
- "none-0000001.pdf")
- document.create_source_directory()
- Path(document.source_path).touch()
-
- document.delete()
+ self.assertEqual(generate_filename(document),
+ "none.pdf")
@override_settings(PAPERLESS_FILENAME_FORMAT="{tags[0]}")
def test_tags_all(self):
document = Document()
- document.file_type = "pdf"
+ document.mime_type = "application/pdf"
document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
document.save()
@@ -274,286 +250,376 @@ class TestDate(TestCase):
document.save()
# Ensure that filename is properly generated
- tmp = document.source_filename
- self.assertEqual(document.generate_source_filename(),
- "demo-0000001.pdf")
- document.create_source_directory()
- Path(document.source_path).touch()
+ self.assertEqual(generate_filename(document),
+ "demo.pdf")
- document.delete()
-
- @override_settings(PAPERLESS_FILENAME_FORMAT="{tags[0]}")
- def test_tags_out_of_bounds_0(self):
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{tags[1]}")
+ def test_tags_out_of_bounds(self):
document = Document()
- document.file_type = "pdf"
+ document.mime_type = "application/pdf"
document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
document.save()
- # Ensure that filename is properly generated
- tmp = document.source_filename
- self.assertEqual(document.generate_source_filename(),
- "none-0000001.pdf")
- document.create_source_directory()
- Path(document.source_path).touch()
-
- document.delete()
-
- @override_settings(PAPERLESS_FILENAME_FORMAT="{tags[10000000]}")
- def test_tags_out_of_bounds_10000000(self):
- document = Document()
- document.file_type = "pdf"
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
+ # Add tag to document
+ document.tags.create(name="demo")
document.save()
# Ensure that filename is properly generated
- tmp = document.source_filename
- self.assertEqual(document.generate_source_filename(),
- "none-0000001.pdf")
- document.create_source_directory()
- Path(document.source_path).touch()
+ self.assertEqual(generate_filename(document),
+ "none.pdf")
- document.delete()
-
- @override_settings(PAPERLESS_FILENAME_FORMAT="{tags[99]}")
- def test_tags_out_of_bounds_99(self):
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{tags}")
+ def test_tags_without_args(self):
document = Document()
- document.file_type = "pdf"
+ document.mime_type = "application/pdf"
document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
document.save()
- # Ensure that filename is properly generated
- tmp = document.source_filename
- self.assertEqual(document.generate_source_filename(),
- "none-0000001.pdf")
- document.create_source_directory()
- Path(document.source_path).touch()
+ self.assertEqual(generate_filename(document), f"{document.pk:07}.pdf")
- document.delete()
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{title} {tag_list}")
+ def test_tag_list(self):
+ doc = Document.objects.create(title="doc1", mime_type="application/pdf")
+ doc.tags.create(name="tag2")
+ doc.tags.create(name="tag1")
- @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/" +
- "{correspondent}/{correspondent}")
+ self.assertEqual(generate_filename(doc), "doc1 tag1,tag2.pdf")
+
+ doc = Document.objects.create(title="doc2", checksum="B", mime_type="application/pdf")
+
+ self.assertEqual(generate_filename(doc), "doc2.pdf")
+
+ @override_settings(PAPERLESS_FILENAME_FORMAT="//etc/something/{title}")
+ def test_filename_relative(self):
+ doc = Document.objects.create(title="doc1", mime_type="application/pdf")
+ doc.filename = generate_filename(doc)
+ doc.save()
+
+ self.assertEqual(doc.source_path, os.path.join(settings.ORIGINALS_DIR, "etc", "something", "doc1.pdf"))
+
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{created_year}-{created_month}-{created_day}")
+ def test_created_year_month_day(self):
+ d1 = timezone.make_aware(datetime.datetime(2020, 3, 6, 1, 1, 1))
+ doc1 = Document.objects.create(title="doc1", mime_type="application/pdf", created=d1)
+
+ self.assertEqual(generate_filename(doc1), "2020-03-06.pdf")
+
+ doc1.created = timezone.make_aware(datetime.datetime(2020, 11, 16, 1, 1, 1))
+
+ self.assertEqual(generate_filename(doc1), "2020-11-16.pdf")
+
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{added_year}-{added_month}-{added_day}")
+ def test_added_year_month_day(self):
+ d1 = timezone.make_aware(datetime.datetime(232, 1, 9, 1, 1, 1))
+ doc1 = Document.objects.create(title="doc1", mime_type="application/pdf", added=d1)
+
+ self.assertEqual(generate_filename(doc1), "232-01-09.pdf")
+
+ doc1.added = timezone.make_aware(datetime.datetime(2020, 11, 16, 1, 1, 1))
+
+ self.assertEqual(generate_filename(doc1), "2020-11-16.pdf")
+
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/{correspondent}/{correspondent}")
def test_nested_directory_cleanup(self):
document = Document()
- document.file_type = "pdf"
+ document.mime_type = "application/pdf"
document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
document.save()
# Ensure that filename is properly generated
- tmp = document.source_filename
- self.assertEqual(document.generate_source_filename(),
- "none/none/none-0000001.pdf")
- document.create_source_directory()
+ document.filename = generate_filename(document)
+ self.assertEqual(document.filename, "none/none/none.pdf")
+ create_source_path_directory(document.source_path)
Path(document.source_path).touch()
# Check proper handling of files
- self.assertEqual(os.path.isdir(settings.MEDIA_ROOT +
- "/documents/originals/none/none"), True)
+ self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR + "/none/none"), True)
+ pk = document.pk
document.delete()
- self.assertEqual(os.path.isfile(settings.MEDIA_ROOT +
- "/documents/originals/none/none/none-0000001.pdf"),
- False)
- self.assertEqual(os.path.isdir(settings.MEDIA_ROOT +
- "/documents/originals/none/none"), False)
- self.assertEqual(os.path.isdir(settings.MEDIA_ROOT +
- "/documents/originals/none"), False)
- self.assertEqual(os.path.isdir(settings.MEDIA_ROOT +
- "/documents/originals"), True)
+ self.assertEqual(os.path.isfile(settings.ORIGINALS_DIR + "/none/none/none.pdf"), False)
+ self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR + "/none/none"), False)
+ self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR + "/none"), False)
+ self.assertEqual(os.path.isdir(settings.ORIGINALS_DIR), True)
@override_settings(PAPERLESS_FILENAME_FORMAT=None)
def test_format_none(self):
document = Document()
- document.file_type = "pdf"
+ document.pk = 1
+ document.mime_type = "application/pdf"
document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
- document.save()
- self.assertEqual(document.generate_source_filename(), "0000001.pdf")
-
- @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/" +
- "{correspondent}")
- def test_document_renamed(self):
- document = Document()
- document.file_type = "pdf"
- document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
- document.save()
-
- # Ensure that filename is properly generated
- tmp = document.source_filename
- self.assertEqual(document.generate_source_filename(),
- "none/none-0000001.pdf")
- document.create_source_directory()
- Path(document.source_path).touch()
-
- # Test source_path
- self.assertEqual(document.source_path, settings.MEDIA_ROOT +
- "/documents/originals/none/none-0000001.pdf")
-
- # Rename the document "illegaly"
- os.makedirs(settings.MEDIA_ROOT + "/documents/originals/test")
- os.rename(settings.MEDIA_ROOT + "/documents/originals/" +
- "none/none-0000001.pdf",
- settings.MEDIA_ROOT + "/documents/originals/" +
- "test/test-0000001.pdf")
- self.assertEqual(os.path.isfile(settings.MEDIA_ROOT + "/documents/" +
- "originals/test/test-0000001.pdf"), True)
- self.assertEqual(os.path.isfile(settings.MEDIA_ROOT + "/documents/" +
- "originals/none/none-0000001.pdf"), False)
-
- # Set new correspondent and expect document to be saved properly
- document.correspondent = Correspondent.objects.get_or_create(
- name="foo")[0]
- document.save()
- self.assertEqual(os.path.isfile(settings.MEDIA_ROOT + "/documents/" +
- "originals/foo/foo-0000001.pdf"), True)
-
- # Check proper handling of files
- self.assertEqual(os.path.isdir(settings.MEDIA_ROOT +
- "/documents/originals/foo"), True)
- self.assertEqual(os.path.isdir(settings.MEDIA_ROOT +
- "/documents/originals/none"), False)
- self.assertEqual(os.path.isdir(settings.MEDIA_ROOT +
- "/documents/originals/test"), False)
- self.assertEqual(document.generate_source_filename(),
- "foo/foo-0000001.pdf")
-
- @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/" +
- "{correspondent}")
- def test_document_renamed_encrypted(self):
- document = Document()
- document.file_type = "pdf"
- document.storage_type = Document.STORAGE_TYPE_GPG
- document.save()
-
- # Ensure that filename is properly generated
- tmp = document.source_filename
- self.assertEqual(document.generate_source_filename(),
- "none/none-0000001.pdf.gpg")
- document.create_source_directory()
- Path(document.source_path).touch()
-
- # Test source_path
- self.assertEqual(document.source_path, settings.MEDIA_ROOT +
- "/documents/originals/none/none-0000001.pdf.gpg")
-
- # Rename the document "illegaly"
- os.makedirs(settings.MEDIA_ROOT + "/documents/originals/test")
- os.rename(settings.MEDIA_ROOT + "/documents/originals/" +
- "none/none-0000001.pdf.gpg",
- settings.MEDIA_ROOT + "/documents/originals/" +
- "test/test-0000001.pdf.gpg")
- self.assertEqual(os.path.isfile(settings.MEDIA_ROOT + "/documents/" +
- "originals/test/test-0000001.pdf.gpg"), True)
- self.assertEqual(os.path.isfile(settings.MEDIA_ROOT + "/documents/" +
- "originals/none/none-0000001.pdf"), False)
-
- # Set new correspondent and expect document to be saved properly
- document.correspondent = Correspondent.objects.get_or_create(
- name="foo")[0]
- document.save()
- self.assertEqual(os.path.isfile(settings.MEDIA_ROOT + "/documents/" +
- "originals/foo/foo-0000001.pdf.gpg"), True)
-
- # Check proper handling of files
- self.assertEqual(os.path.isdir(settings.MEDIA_ROOT +
- "/documents/originals/foo"), True)
- self.assertEqual(os.path.isdir(settings.MEDIA_ROOT +
- "/documents/originals/none"), False)
- self.assertEqual(os.path.isdir(settings.MEDIA_ROOT +
- "/documents/originals/test"), False)
- self.assertEqual(document.generate_source_filename(),
- "foo/foo-0000001.pdf.gpg")
-
- def test_delete_all_empty_subdirectories(self):
- # Create our working directory
- tmp = "/tmp/paperless-tests-{}".format(str(uuid4())[:8])
- os.makedirs(tmp)
- self.add_to_deletion_list(tmp)
-
- os.makedirs(os.path.join(tmp, "empty"))
- os.makedirs(os.path.join(tmp, "empty", "subdirectory"))
-
- os.makedirs(os.path.join(tmp, "notempty"))
- Path(os.path.join(tmp, "notempty", "file")).touch()
-
- Document.delete_all_empty_subdirectories(tmp)
-
- self.assertEqual(os.path.isdir(os.path.join(tmp, "notempty")), True)
- self.assertEqual(os.path.isdir(os.path.join(tmp, "empty")), False)
- self.assertEqual(os.path.isfile(
- os.path.join(tmp, "notempty", "file")), True)
+ self.assertEqual(generate_filename(document), "0000001.pdf")
def test_try_delete_empty_directories(self):
# Create our working directory
- tmp = "/tmp/paperless-tests-{}".format(str(uuid4())[:8])
+ tmp = os.path.join(settings.ORIGINALS_DIR, "test_delete_empty")
os.makedirs(tmp)
- self.add_to_deletion_list(tmp)
os.makedirs(os.path.join(tmp, "notempty"))
Path(os.path.join(tmp, "notempty", "file")).touch()
os.makedirs(os.path.join(tmp, "notempty", "empty"))
- Document.try_delete_empty_directories(
- os.path.join(tmp, "notempty", "empty"))
+ delete_empty_directories(os.path.join(tmp, "notempty", "empty"), root=settings.ORIGINALS_DIR)
self.assertEqual(os.path.isdir(os.path.join(tmp, "notempty")), True)
self.assertEqual(os.path.isfile(
os.path.join(tmp, "notempty", "file")), True)
self.assertEqual(os.path.isdir(
os.path.join(tmp, "notempty", "empty")), False)
- @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/" +
- "{correspondent}")
- def test_document_accidentally_deleted(self):
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{created/[title]")
+ def test_invalid_format(self):
document = Document()
- document.file_type = "pdf"
+ document.pk = 1
+ document.mime_type = "application/pdf"
document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
- document.save()
- # Ensure that filename is properly generated
- tmp = document.source_filename
- self.assertEqual(document.generate_source_filename(),
- "none/none-0000001.pdf")
- document.create_source_directory()
- Path(document.source_path).touch()
+ self.assertEqual(generate_filename(document), "0000001.pdf")
- # Test source_path
- self.assertEqual(document.source_path, settings.MEDIA_ROOT +
- "/documents/originals/none/none-0000001.pdf")
-
- # Delete the document "illegaly"
- os.remove(settings.MEDIA_ROOT + "/documents/originals/" +
- "none/none-0000001.pdf")
-
- # Set new correspondent and expect document to be saved properly
- document.correspondent = Correspondent.objects.get_or_create(
- name="foo")[0]
- document.save()
-
- # Check proper handling of files
- self.assertEqual(os.path.isdir(settings.MEDIA_ROOT +
- "/documents/originals/none"), True)
- self.assertEqual(document.source_filename,
- "none/none-0000001.pdf")
-
- @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/" +
- "{correspondent}")
- def test_set_filename(self):
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{created__year}")
+ def test_invalid_format_key(self):
document = Document()
- document.file_type = "pdf"
+ document.pk = 1
+ document.mime_type = "application/pdf"
document.storage_type = Document.STORAGE_TYPE_UNENCRYPTED
+
+ self.assertEqual(generate_filename(document), "0000001.pdf")
+
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{title}")
+ def test_duplicates(self):
+ document = Document.objects.create(mime_type="application/pdf", title="qwe", checksum="A", pk=1)
+ document2 = Document.objects.create(mime_type="application/pdf", title="qwe", checksum="B", pk=2)
+ Path(document.source_path).touch()
+ Path(document2.source_path).touch()
+ document.filename = "0000001.pdf"
document.save()
- # Ensure that filename is properly generated
- tmp = document.source_filename
- self.assertEqual(document.generate_source_filename(),
- "none/none-0000001.pdf")
- document.create_source_directory()
- Path(document.source_path).touch()
+ self.assertTrue(os.path.isfile(document.source_path))
+ self.assertEqual(document.filename, "qwe.pdf")
- # Set existing filename
- document.set_filename(tmp)
- self.assertEqual(document.source_filename, "none/none-0000001.pdf")
+ document2.filename = "0000002.pdf"
+ document2.save()
- # Set non-existing filename
- document.set_filename("doesnotexist")
- self.assertEqual(document.source_filename, "none/none-0000001.pdf")
+ self.assertTrue(os.path.isfile(document.source_path))
+ self.assertEqual(document2.filename, "qwe_01.pdf")
+
+ # saving should not change the file names.
+
+ document.save()
+
+ self.assertTrue(os.path.isfile(document.source_path))
+ self.assertEqual(document.filename, "qwe.pdf")
+
+ document2.save()
+
+ self.assertTrue(os.path.isfile(document.source_path))
+ self.assertEqual(document2.filename, "qwe_01.pdf")
+
+ document.delete()
+
+ self.assertFalse(os.path.isfile(document.source_path))
+
+ # filename free, should remove _01 suffix
+
+ document2.save()
+
+ self.assertTrue(os.path.isfile(document.source_path))
+ self.assertEqual(document2.filename, "qwe.pdf")
+
+
+
+class TestFileHandlingWithArchive(DirectoriesMixin, TestCase):
+
+ @override_settings(PAPERLESS_FILENAME_FORMAT=None)
+ def test_create_no_format(self):
+ original = os.path.join(settings.ORIGINALS_DIR, "0000001.pdf")
+ archive = os.path.join(settings.ARCHIVE_DIR, "0000001.pdf")
+ Path(original).touch()
+ Path(archive).touch()
+ doc = Document.objects.create(mime_type="application/pdf", filename="0000001.pdf", checksum="A", archive_checksum="B")
+
+ self.assertTrue(os.path.isfile(original))
+ self.assertTrue(os.path.isfile(archive))
+ self.assertTrue(os.path.isfile(doc.source_path))
+ self.assertTrue(os.path.isfile(doc.archive_path))
+
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/{title}")
+ def test_create_with_format(self):
+ original = os.path.join(settings.ORIGINALS_DIR, "0000001.pdf")
+ archive = os.path.join(settings.ARCHIVE_DIR, "0000001.pdf")
+ Path(original).touch()
+ Path(archive).touch()
+ doc = Document.objects.create(mime_type="application/pdf", title="my_doc", filename="0000001.pdf", checksum="A", archive_checksum="B")
+
+ self.assertFalse(os.path.isfile(original))
+ self.assertFalse(os.path.isfile(archive))
+ self.assertTrue(os.path.isfile(doc.source_path))
+ self.assertTrue(os.path.isfile(doc.archive_path))
+ self.assertEqual(doc.source_path, os.path.join(settings.ORIGINALS_DIR, "none", "my_doc.pdf"))
+ self.assertEqual(doc.archive_path, os.path.join(settings.ARCHIVE_DIR, "none", "my_doc.pdf"))
+
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/{title}")
+ def test_move_archive_gone(self):
+ original = os.path.join(settings.ORIGINALS_DIR, "0000001.pdf")
+ archive = os.path.join(settings.ARCHIVE_DIR, "0000001.pdf")
+ Path(original).touch()
+ doc = Document.objects.create(mime_type="application/pdf", title="my_doc", filename="0000001.pdf", checksum="A", archive_checksum="B")
+
+ self.assertTrue(os.path.isfile(original))
+ self.assertFalse(os.path.isfile(archive))
+ self.assertTrue(os.path.isfile(doc.source_path))
+ self.assertFalse(os.path.isfile(doc.archive_path))
+
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/{title}")
+ def test_move_archive_exists(self):
+ original = os.path.join(settings.ORIGINALS_DIR, "0000001.pdf")
+ archive = os.path.join(settings.ARCHIVE_DIR, "0000001.pdf")
+ Path(original).touch()
+ Path(archive).touch()
+ os.makedirs(os.path.join(settings.ARCHIVE_DIR, "none"))
+ Path(os.path.join(settings.ARCHIVE_DIR, "none", "my_doc.pdf")).touch()
+ doc = Document.objects.create(mime_type="application/pdf", title="my_doc", filename="0000001.pdf", checksum="A", archive_checksum="B")
+
+ self.assertTrue(os.path.isfile(original))
+ self.assertTrue(os.path.isfile(archive))
+ self.assertTrue(os.path.isfile(doc.source_path))
+ self.assertTrue(os.path.isfile(doc.archive_path))
+
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/{title}")
+ @mock.patch("documents.signals.handlers.os.rename")
+ def test_move_archive_error(self, m):
+
+ def fake_rename(src, dst):
+ if "archive" in src:
+ raise OSError()
+ else:
+ os.remove(src)
+ Path(dst).touch()
+
+ m.side_effect = fake_rename
+
+ original = os.path.join(settings.ORIGINALS_DIR, "0000001.pdf")
+ archive = os.path.join(settings.ARCHIVE_DIR, "0000001.pdf")
+ Path(original).touch()
+ Path(archive).touch()
+ doc = Document.objects.create(mime_type="application/pdf", title="my_doc", filename="0000001.pdf", checksum="A", archive_checksum="B")
+
+ self.assertTrue(os.path.isfile(original))
+ self.assertTrue(os.path.isfile(archive))
+ self.assertTrue(os.path.isfile(doc.source_path))
+ self.assertTrue(os.path.isfile(doc.archive_path))
+
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/{title}")
+ def test_move_file_gone(self):
+ original = os.path.join(settings.ORIGINALS_DIR, "0000001.pdf")
+ archive = os.path.join(settings.ARCHIVE_DIR, "0000001.pdf")
+ #Path(original).touch()
+ Path(archive).touch()
+ doc = Document.objects.create(mime_type="application/pdf", title="my_doc", filename="0000001.pdf", checksum="A", archive_checksum="B")
+
+ self.assertFalse(os.path.isfile(original))
+ self.assertTrue(os.path.isfile(archive))
+ self.assertFalse(os.path.isfile(doc.source_path))
+ self.assertTrue(os.path.isfile(doc.archive_path))
+
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/{title}")
+ @mock.patch("documents.signals.handlers.os.rename")
+ def test_move_file_error(self, m):
+
+ def fake_rename(src, dst):
+ if "original" in src:
+ raise OSError()
+ else:
+ os.remove(src)
+ Path(dst).touch()
+
+ m.side_effect = fake_rename
+
+ original = os.path.join(settings.ORIGINALS_DIR, "0000001.pdf")
+ archive = os.path.join(settings.ARCHIVE_DIR, "0000001.pdf")
+ Path(original).touch()
+ Path(archive).touch()
+ doc = Document.objects.create(mime_type="application/pdf", title="my_doc", filename="0000001.pdf", checksum="A", archive_checksum="B")
+
+ self.assertTrue(os.path.isfile(original))
+ self.assertTrue(os.path.isfile(archive))
+ self.assertTrue(os.path.isfile(doc.source_path))
+ self.assertTrue(os.path.isfile(doc.archive_path))
+
+ def test_archive_deleted(self):
+ original = os.path.join(settings.ORIGINALS_DIR, "0000001.pdf")
+ archive = os.path.join(settings.ARCHIVE_DIR, "0000001.pdf")
+ Path(original).touch()
+ Path(archive).touch()
+ doc = Document.objects.create(mime_type="application/pdf", title="my_doc", filename="0000001.pdf", checksum="A", archive_checksum="B")
+
+ self.assertTrue(os.path.isfile(original))
+ self.assertTrue(os.path.isfile(archive))
+ self.assertTrue(os.path.isfile(doc.source_path))
+ self.assertTrue(os.path.isfile(doc.archive_path))
+
+ doc.delete()
+
+ self.assertFalse(os.path.isfile(original))
+ self.assertFalse(os.path.isfile(archive))
+ self.assertFalse(os.path.isfile(doc.source_path))
+ self.assertFalse(os.path.isfile(doc.archive_path))
+
+ @override_settings(PAPERLESS_FILENAME_FORMAT="{correspondent}/{title}")
+ def test_database_error(self):
+
+ original = os.path.join(settings.ORIGINALS_DIR, "0000001.pdf")
+ archive = os.path.join(settings.ARCHIVE_DIR, "0000001.pdf")
+ Path(original).touch()
+ Path(archive).touch()
+ doc = Document(mime_type="application/pdf", title="my_doc", filename="0000001.pdf", checksum="A", archive_checksum="B")
+ with mock.patch("documents.signals.handlers.Document.objects.filter") as m:
+ m.side_effect = DatabaseError()
+ doc.save()
+
+ self.assertTrue(os.path.isfile(original))
+ self.assertTrue(os.path.isfile(archive))
+ self.assertTrue(os.path.isfile(doc.source_path))
+ self.assertTrue(os.path.isfile(doc.archive_path))
+
+class TestFilenameGeneration(TestCase):
+
+ @override_settings(
+ PAPERLESS_FILENAME_FORMAT="{title}"
+ )
+ def test_invalid_characters(self):
+
+ doc = Document.objects.create(title="This. is the title.", mime_type="application/pdf", pk=1, checksum="1")
+ self.assertEqual(generate_filename(doc), "This. is the title.pdf")
+
+ doc = Document.objects.create(title="my\\invalid/../title:yay", mime_type="application/pdf", pk=2, checksum="2")
+ self.assertEqual(generate_filename(doc), "my-invalid-..-title-yay.pdf")
+
+ @override_settings(
+ PAPERLESS_FILENAME_FORMAT="{created}"
+ )
+ def test_date(self):
+ doc = Document.objects.create(title="does not matter", created=timezone.make_aware(datetime.datetime(2020,5,21, 7,36,51, 153)), mime_type="application/pdf", pk=2, checksum="2")
+ self.assertEqual(generate_filename(doc), "2020-05-21.pdf")
+
+
+def run():
+ doc = Document.objects.create(checksum=str(uuid.uuid4()), title=str(uuid.uuid4()), content="wow")
+ doc.filename = generate_unique_filename(doc, settings.ORIGINALS_DIR)
+ Path(doc.thumbnail_path).touch()
+ with open(doc.source_path, "w") as f:
+ f.write(str(uuid.uuid4()))
+ with open(doc.source_path, "rb") as f:
+ doc.checksum = hashlib.md5(f.read()).hexdigest()
+
+ with open(doc.archive_path, "w") as f:
+ f.write(str(uuid.uuid4()))
+ with open(doc.archive_path, "rb") as f:
+ doc.archive_checksum = hashlib.md5(f.read()).hexdigest()
+
+ doc.save()
+
+ for i in range(30):
+ doc.title = str(random.randrange(1, 5))
+ doc.save()
diff --git a/src/documents/tests/test_importer.py b/src/documents/tests/test_importer.py
index 0efddbd71..01600df33 100644
--- a/src/documents/tests/test_importer.py
+++ b/src/documents/tests/test_importer.py
@@ -1,9 +1,8 @@
from django.core.management.base import CommandError
from django.test import TestCase
-from ..management.commands.document_importer import Command
-
from documents.settings import EXPORTER_FILE_NAME
+from ..management.commands.document_importer import Command
class TestImporter(TestCase):
diff --git a/src/documents/tests/test_index.py b/src/documents/tests/test_index.py
new file mode 100644
index 000000000..2baa9621d
--- /dev/null
+++ b/src/documents/tests/test_index.py
@@ -0,0 +1,35 @@
+from django.test import TestCase
+
+from documents import index
+from documents.index import JsonFormatter
+from documents.models import Document
+from documents.tests.utils import DirectoriesMixin
+
+
+class JsonFormatterTest(TestCase):
+
+ def setUp(self) -> None:
+ self.formatter = JsonFormatter()
+
+ def test_empty_fragments(self):
+ self.assertListEqual(self.formatter.format([]), [])
+
+
+class TestAutoComplete(DirectoriesMixin, TestCase):
+
+ def test_auto_complete(self):
+
+ doc1 = Document.objects.create(title="doc1", checksum="A", content="test test2 test3")
+ doc2 = Document.objects.create(title="doc2", checksum="B", content="test test2")
+ doc3 = Document.objects.create(title="doc3", checksum="C", content="test2")
+
+ index.add_or_update_document(doc1)
+ index.add_or_update_document(doc2)
+ index.add_or_update_document(doc3)
+
+ ix = index.open_index()
+
+ self.assertListEqual(index.autocomplete(ix, "tes"), [b"test3", b"test", b"test2"])
+ self.assertListEqual(index.autocomplete(ix, "tes", limit=3), [b"test3", b"test", b"test2"])
+ self.assertListEqual(index.autocomplete(ix, "tes", limit=1), [b"test3"])
+ self.assertListEqual(index.autocomplete(ix, "tes", limit=0), [])
diff --git a/src/documents/tests/test_logger.py b/src/documents/tests/test_logger.py
index 9b14a3902..bbc9c2b5d 100644
--- a/src/documents/tests/test_logger.py
+++ b/src/documents/tests/test_logger.py
@@ -1,9 +1,8 @@
import logging
import uuid
-
from unittest import mock
-from django.test import TestCase
+from django.test import TestCase, override_settings
from ..models import Log
@@ -15,6 +14,7 @@ class TestPaperlessLog(TestCase):
self.logger = logging.getLogger(
"documents.management.commands.document_consumer")
+ @override_settings(DISABLE_DBHANDLER=False)
def test_that_it_saves_at_all(self):
kw = {"group": uuid.uuid4()}
@@ -25,20 +25,21 @@ class TestPaperlessLog(TestCase):
# Debug messages are ignored by default
self.logger.debug("This is a debugging message", extra=kw)
- self.assertEqual(Log.objects.all().count(), 0)
-
- self.logger.info("This is an informational message", extra=kw)
self.assertEqual(Log.objects.all().count(), 1)
- self.logger.warning("This is an warning message", extra=kw)
+ self.logger.info("This is an informational message", extra=kw)
self.assertEqual(Log.objects.all().count(), 2)
- self.logger.error("This is an error message", extra=kw)
+ self.logger.warning("This is an warning message", extra=kw)
self.assertEqual(Log.objects.all().count(), 3)
- self.logger.critical("This is a critical message", extra=kw)
+ self.logger.error("This is an error message", extra=kw)
self.assertEqual(Log.objects.all().count(), 4)
+ self.logger.critical("This is a critical message", extra=kw)
+ self.assertEqual(Log.objects.all().count(), 5)
+
+ @override_settings(DISABLE_DBHANDLER=False)
def test_groups(self):
kw1 = {"group": uuid.uuid4()}
@@ -48,10 +49,6 @@ class TestPaperlessLog(TestCase):
with mock.patch("logging.StreamHandler.emit") as __:
- # Debug messages are ignored by default
- self.logger.debug("This is a debugging message", extra=kw1)
- self.assertEqual(Log.objects.all().count(), 0)
-
self.logger.info("This is an informational message", extra=kw2)
self.assertEqual(Log.objects.all().count(), 1)
self.assertEqual(Log.objects.filter(group=kw2["group"]).count(), 1)
@@ -67,18 +64,3 @@ class TestPaperlessLog(TestCase):
self.logger.critical("This is a critical message", extra=kw1)
self.assertEqual(Log.objects.all().count(), 4)
self.assertEqual(Log.objects.filter(group=kw1["group"]).count(), 2)
-
- def test_groupped_query(self):
-
- kw = {"group": uuid.uuid4()}
- with mock.patch("logging.StreamHandler.emit") as __:
- self.logger.info("Message 0", extra=kw)
- self.logger.info("Message 1", extra=kw)
- self.logger.info("Message 2", extra=kw)
- self.logger.info("Message 3", extra=kw)
-
- self.assertEqual(Log.objects.all().by_group().count(), 1)
- self.assertEqual(
- Log.objects.all().by_group()[0]["messages"],
- "Message 0\nMessage 1\nMessage 2\nMessage 3"
- )
diff --git a/src/documents/tests/test_mail.py b/src/documents/tests/test_mail.py
deleted file mode 100644
index 3e78cd23b..000000000
--- a/src/documents/tests/test_mail.py
+++ /dev/null
@@ -1,91 +0,0 @@
-import base64
-import os
-import magic
-
-from hashlib import md5
-from unittest import mock
-
-from django.conf import settings
-from django.test import TestCase
-
-from ..mail import Message, Attachment
-
-
-class TestMessage(TestCase):
-
- def __init__(self, *args, **kwargs):
-
- TestCase.__init__(self, *args, **kwargs)
- self.sample = os.path.join(
- settings.BASE_DIR,
- "documents",
- "tests",
- "samples",
- "mail.txt"
- )
-
- def test_init(self):
-
- with open(self.sample, "rb") as f:
-
- with mock.patch("logging.StreamHandler.emit") as __:
- message = Message(f.read())
-
- self.assertTrue(message)
- self.assertEqual(message.subject, "Test 0")
-
- data = message.attachment.read()
-
- self.assertEqual(
- md5(data).hexdigest(), "7c89655f9e9eb7dd8cde8568e8115d59")
-
- self.assertEqual(
- message.attachment.content_type, "application/pdf")
- with magic.Magic(flags=magic.MAGIC_MIME_TYPE) as m:
- self.assertEqual(m.id_buffer(data), "application/pdf")
-
-
-class TestInlineMessage(TestCase):
-
- def __init__(self, *args, **kwargs):
-
- TestCase.__init__(self, *args, **kwargs)
- self.sample = os.path.join(
- settings.BASE_DIR,
- "documents",
- "tests",
- "samples",
- "inline_mail.txt"
- )
-
- def test_init(self):
-
- with open(self.sample, "rb") as f:
-
- with mock.patch("logging.StreamHandler.emit") as __:
- message = Message(f.read())
-
- self.assertTrue(message)
- self.assertEqual(message.subject, "Paperless Inline Image")
-
- data = message.attachment.read()
-
- self.assertEqual(
- md5(data).hexdigest(), "30c00a7b42913e65f7fdb0be40b9eef3")
-
- self.assertEqual(
- message.attachment.content_type, "image/png")
- with magic.Magic(flags=magic.MAGIC_MIME_TYPE) as m:
- self.assertEqual(m.id_buffer(data), "image/png")
-
-
-class TestAttachment(TestCase):
-
- def test_init(self):
- data = base64.encodebytes(b"0")
- self.assertEqual(Attachment(data, "application/pdf").suffix, "pdf")
- self.assertEqual(Attachment(data, "image/png").suffix, "png")
- self.assertEqual(Attachment(data, "image/jpeg").suffix, "jpeg")
- self.assertEqual(Attachment(data, "image/gif").suffix, "gif")
- self.assertEqual(Attachment(data, "image/tiff").suffix, "tiff")
- self.assertEqual(Attachment(data, "image/png").read(), data)
diff --git a/src/documents/tests/test_management_archiver.py b/src/documents/tests/test_management_archiver.py
new file mode 100644
index 000000000..0828f05ff
--- /dev/null
+++ b/src/documents/tests/test_management_archiver.py
@@ -0,0 +1,40 @@
+import filecmp
+import os
+import shutil
+
+from django.core.management import call_command
+from django.test import TestCase
+
+from documents.management.commands.document_archiver import handle_document
+from documents.models import Document
+from documents.tests.utils import DirectoriesMixin
+
+
+sample_file = os.path.join(os.path.dirname(__file__), "samples", "simple.pdf")
+
+
+class TestArchiver(DirectoriesMixin, TestCase):
+
+ def make_models(self):
+ return Document.objects.create(checksum="A", title="A", content="first document", mime_type="application/pdf")
+
+ def test_archiver(self):
+
+ doc = self.make_models()
+ shutil.copy(sample_file, os.path.join(self.dirs.originals_dir, f"{doc.id:07}.pdf"))
+
+ call_command('document_archiver')
+
+ def test_handle_document(self):
+
+ doc = self.make_models()
+ shutil.copy(sample_file, os.path.join(self.dirs.originals_dir, f"{doc.id:07}.pdf"))
+
+ handle_document(doc.pk)
+
+ doc = Document.objects.get(id=doc.id)
+
+ self.assertIsNotNone(doc.checksum)
+ self.assertTrue(os.path.isfile(doc.archive_path))
+ self.assertTrue(os.path.isfile(doc.source_path))
+ self.assertTrue(filecmp.cmp(sample_file, doc.source_path))
diff --git a/src/documents/tests/test_management_consumer.py b/src/documents/tests/test_management_consumer.py
new file mode 100644
index 000000000..b6a61a167
--- /dev/null
+++ b/src/documents/tests/test_management_consumer.py
@@ -0,0 +1,262 @@
+import filecmp
+import os
+import shutil
+from threading import Thread
+from time import sleep
+from unittest import mock
+
+from django.conf import settings
+from django.core.management import call_command, CommandError
+from django.test import override_settings, TransactionTestCase
+
+from documents.models import Tag
+from documents.consumer import ConsumerError
+from documents.management.commands import document_consumer
+from documents.tests.utils import DirectoriesMixin
+
+
+class ConsumerThread(Thread):
+
+ def __init__(self):
+ super().__init__()
+ self.cmd = document_consumer.Command()
+
+ def run(self) -> None:
+ self.cmd.handle(directory=settings.CONSUMPTION_DIR, oneshot=False)
+
+ def stop(self):
+ # Consumer checks this every second.
+ self.cmd.stop_flag = True
+
+
+def chunked(size, source):
+ for i in range(0, len(source), size):
+ yield source[i:i+size]
+
+
+class ConsumerMixin:
+
+ sample_file = os.path.join(os.path.dirname(__file__), "samples", "simple.pdf")
+
+ def setUp(self) -> None:
+ super(ConsumerMixin, self).setUp()
+ self.t = None
+ patcher = mock.patch("documents.management.commands.document_consumer.async_task")
+ self.task_mock = patcher.start()
+ self.addCleanup(patcher.stop)
+
+ def t_start(self):
+ self.t = ConsumerThread()
+ self.t.start()
+ # give the consumer some time to do initial work
+ sleep(1)
+
+ def tearDown(self) -> None:
+ if self.t:
+ # set the stop flag
+ self.t.stop()
+ # wait for the consumer to exit.
+ self.t.join()
+
+ super(ConsumerMixin, self).tearDown()
+
+ def wait_for_task_mock_call(self):
+ n = 0
+ while n < 100:
+ if self.task_mock.call_count > 0:
+ # give task_mock some time to finish and raise errors
+ sleep(1)
+ return
+ n += 1
+ sleep(0.1)
+
+ # A bogus async_task that will simply check the file for
+ # completeness and raise an exception otherwise.
+ def bogus_task(self, func, filename, **kwargs):
+ eq = filecmp.cmp(filename, self.sample_file, shallow=False)
+ if not eq:
+ print("Consumed an INVALID file.")
+ raise ConsumerError("Incomplete File READ FAILED")
+ else:
+ print("Consumed a perfectly valid file.")
+
+ def slow_write_file(self, target, incomplete=False):
+ with open(self.sample_file, 'rb') as f:
+ pdf_bytes = f.read()
+
+ if incomplete:
+ pdf_bytes = pdf_bytes[:len(pdf_bytes) - 100]
+
+ with open(target, 'wb') as f:
+ # this will take 2 seconds, since the file is about 20k.
+ print("Start writing file.")
+ for b in chunked(1000, pdf_bytes):
+ f.write(b)
+ sleep(0.1)
+ print("file completed.")
+
+
+class TestConsumer(DirectoriesMixin, ConsumerMixin, TransactionTestCase):
+
+ def test_consume_file(self):
+ self.t_start()
+
+ f = os.path.join(self.dirs.consumption_dir, "my_file.pdf")
+ shutil.copy(self.sample_file, f)
+
+ self.wait_for_task_mock_call()
+
+ self.task_mock.assert_called_once()
+
+ args, kwargs = self.task_mock.call_args
+ self.assertEqual(args[1], f)
+
+ def test_consume_file_invalid_ext(self):
+ self.t_start()
+
+ f = os.path.join(self.dirs.consumption_dir, "my_file.wow")
+ shutil.copy(self.sample_file, f)
+
+ self.wait_for_task_mock_call()
+
+ self.task_mock.assert_not_called()
+
+ def test_consume_existing_file(self):
+ f = os.path.join(self.dirs.consumption_dir, "my_file.pdf")
+ shutil.copy(self.sample_file, f)
+
+ self.t_start()
+ self.task_mock.assert_called_once()
+
+ args, kwargs = self.task_mock.call_args
+ self.assertEqual(args[1], f)
+
+ @mock.patch("documents.management.commands.document_consumer.logger.error")
+ def test_slow_write_pdf(self, error_logger):
+
+ self.task_mock.side_effect = self.bogus_task
+
+ self.t_start()
+
+ fname = os.path.join(self.dirs.consumption_dir, "my_file.pdf")
+
+ self.slow_write_file(fname)
+
+ self.wait_for_task_mock_call()
+
+ error_logger.assert_not_called()
+
+ self.task_mock.assert_called_once()
+
+ args, kwargs = self.task_mock.call_args
+ self.assertEqual(args[1], fname)
+
+ @mock.patch("documents.management.commands.document_consumer.logger.error")
+ def test_slow_write_and_move(self, error_logger):
+
+ self.task_mock.side_effect = self.bogus_task
+
+ self.t_start()
+
+ fname = os.path.join(self.dirs.consumption_dir, "my_file.~df")
+ fname2 = os.path.join(self.dirs.consumption_dir, "my_file.pdf")
+
+ self.slow_write_file(fname)
+ shutil.move(fname, fname2)
+
+ self.wait_for_task_mock_call()
+
+ self.task_mock.assert_called_once()
+
+ args, kwargs = self.task_mock.call_args
+ self.assertEqual(args[1], fname2)
+
+ error_logger.assert_not_called()
+
+ @mock.patch("documents.management.commands.document_consumer.logger.error")
+ def test_slow_write_incomplete(self, error_logger):
+
+ self.task_mock.side_effect = self.bogus_task
+
+ self.t_start()
+
+ fname = os.path.join(self.dirs.consumption_dir, "my_file.pdf")
+ self.slow_write_file(fname, incomplete=True)
+
+ self.wait_for_task_mock_call()
+
+ self.task_mock.assert_called_once()
+ args, kwargs = self.task_mock.call_args
+ self.assertEqual(args[1], fname)
+
+ # assert that we have an error logged with this invalid file.
+ error_logger.assert_called_once()
+
+ @override_settings(CONSUMPTION_DIR="does_not_exist")
+ def test_consumption_directory_invalid(self):
+
+ self.assertRaises(CommandError, call_command, 'document_consumer', '--oneshot')
+
+ @override_settings(CONSUMPTION_DIR="")
+ def test_consumption_directory_unset(self):
+
+ self.assertRaises(CommandError, call_command, 'document_consumer', '--oneshot')
+
+
+@override_settings(CONSUMER_POLLING=1)
+class TestConsumerPolling(TestConsumer):
+ # just do all the tests with polling
+ pass
+
+
+@override_settings(CONSUMER_RECURSIVE=True)
+class TestConsumerRecursive(TestConsumer):
+ # just do all the tests with recursive
+ pass
+
+
+@override_settings(CONSUMER_RECURSIVE=True)
+@override_settings(CONSUMER_POLLING=1)
+class TestConsumerRecursivePolling(TestConsumer):
+ # just do all the tests with polling and recursive
+ pass
+
+
+class TestConsumerTags(DirectoriesMixin, ConsumerMixin, TransactionTestCase):
+
+ @override_settings(CONSUMER_RECURSIVE=True)
+ @override_settings(CONSUMER_SUBDIRS_AS_TAGS=True)
+ def test_consume_file_with_path_tags(self):
+
+ tag_names = ("existingTag", "Space Tag")
+ # Create a Tag prior to consuming a file using it in path
+ tag_ids = [Tag.objects.create(name="existingtag").pk,]
+
+ self.t_start()
+
+ path = os.path.join(self.dirs.consumption_dir, *tag_names)
+ os.makedirs(path, exist_ok=True)
+ f = os.path.join(path, "my_file.pdf")
+ # Wait at least inotify read_delay for recursive watchers
+ # to be created for the new directories
+ sleep(1)
+ shutil.copy(self.sample_file, f)
+
+ self.wait_for_task_mock_call()
+
+ self.task_mock.assert_called_once()
+
+ # Add the pk of the Tag created by _consume()
+ tag_ids.append(Tag.objects.get(name=tag_names[1]).pk)
+
+ args, kwargs = self.task_mock.call_args
+ self.assertEqual(args[1], f)
+
+ # assertCountEqual has a bad name, but test that the first
+ # sequence contains the same elements as second, regardless of
+ # their order.
+ self.assertCountEqual(kwargs["override_tag_ids"], tag_ids)
+
+ @override_settings(CONSUMER_POLLING=1)
+ def test_consume_file_with_path_tags_polling(self):
+ self.test_consume_file_with_path_tags()
diff --git a/src/documents/tests/test_management_decrypt.py b/src/documents/tests/test_management_decrypt.py
new file mode 100644
index 000000000..1d64b1105
--- /dev/null
+++ b/src/documents/tests/test_management_decrypt.py
@@ -0,0 +1,57 @@
+import hashlib
+import json
+import os
+import shutil
+import tempfile
+from unittest import mock
+
+from django.core.management import call_command
+from django.test import TestCase, override_settings
+
+from documents.management.commands import document_exporter
+from documents.models import Document, Tag, DocumentType, Correspondent
+
+
+class TestDecryptDocuments(TestCase):
+
+ @override_settings(
+ ORIGINALS_DIR=os.path.join(os.path.dirname(__file__), "samples", "originals"),
+ THUMBNAIL_DIR=os.path.join(os.path.dirname(__file__), "samples", "thumb"),
+ PASSPHRASE="test",
+ PAPERLESS_FILENAME_FORMAT=None
+ )
+ @mock.patch("documents.management.commands.decrypt_documents.input")
+ def test_decrypt(self, m):
+
+ media_dir = tempfile.mkdtemp()
+ originals_dir = os.path.join(media_dir, "documents", "originals")
+ thumb_dir = os.path.join(media_dir, "documents", "thumbnails")
+ os.makedirs(originals_dir, exist_ok=True)
+ os.makedirs(thumb_dir, exist_ok=True)
+
+ override_settings(
+ ORIGINALS_DIR=originals_dir,
+ THUMBNAIL_DIR=thumb_dir,
+ PASSPHRASE="test"
+ ).enable()
+
+ doc = Document.objects.create(checksum="9c9691e51741c1f4f41a20896af31770", title="wow", filename="0000002.pdf.gpg", mime_type="application/pdf", storage_type=Document.STORAGE_TYPE_GPG)
+
+ shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "documents", "originals", "0000002.pdf.gpg"), os.path.join(originals_dir, "0000002.pdf.gpg"))
+ shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "documents", "thumbnails", f"0000002.png.gpg"), os.path.join(thumb_dir, f"{doc.id:07}.png.gpg"))
+
+ call_command('decrypt_documents')
+
+ doc.refresh_from_db()
+
+ self.assertEqual(doc.storage_type, Document.STORAGE_TYPE_UNENCRYPTED)
+ self.assertEqual(doc.filename, "0000002.pdf")
+ self.assertTrue(os.path.isfile(os.path.join(originals_dir, "0000002.pdf")))
+ self.assertTrue(os.path.isfile(doc.source_path))
+ self.assertTrue(os.path.isfile(os.path.join(thumb_dir, f"{doc.id:07}.png")))
+ self.assertTrue(os.path.isfile(doc.thumbnail_path))
+
+ with doc.source_file as f:
+ checksum = hashlib.md5(f.read()).hexdigest()
+ self.assertEqual(checksum, doc.checksum)
+
diff --git a/src/documents/tests/test_management_exporter.py b/src/documents/tests/test_management_exporter.py
new file mode 100644
index 000000000..22d6fc7f6
--- /dev/null
+++ b/src/documents/tests/test_management_exporter.py
@@ -0,0 +1,72 @@
+import hashlib
+import json
+import os
+import shutil
+import tempfile
+
+from django.core.management import call_command
+from django.test import TestCase, override_settings
+
+from documents.management.commands import document_exporter
+from documents.models import Document, Tag, DocumentType, Correspondent
+from documents.sanity_checker import check_sanity
+from documents.tests.utils import DirectoriesMixin, paperless_environment
+
+
+class TestExportImport(DirectoriesMixin, TestCase):
+
+ @override_settings(
+ PASSPHRASE="test"
+ )
+ def test_exporter(self):
+ shutil.rmtree(os.path.join(self.dirs.media_dir, "documents"))
+ shutil.copytree(os.path.join(os.path.dirname(__file__), "samples", "documents"), os.path.join(self.dirs.media_dir, "documents"))
+
+ file = os.path.join(self.dirs.originals_dir, "0000001.pdf")
+
+ Document.objects.create(content="Content", checksum="42995833e01aea9b3edee44bbfdd7ce1", archive_checksum="62acb0bcbfbcaa62ca6ad3668e4e404b", title="wow", filename="0000001.pdf", mime_type="application/pdf")
+ Document.objects.create(content="Content", checksum="9c9691e51741c1f4f41a20896af31770", title="wow", filename="0000002.pdf.gpg", mime_type="application/pdf", storage_type=Document.STORAGE_TYPE_GPG)
+ Tag.objects.create(name="t")
+ DocumentType.objects.create(name="dt")
+ Correspondent.objects.create(name="c")
+
+ target = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, target)
+
+ call_command('document_exporter', target)
+
+ with open(os.path.join(target, "manifest.json")) as f:
+ manifest = json.load(f)
+
+ self.assertEqual(len(manifest), 5)
+
+ for element in manifest:
+ if element['model'] == 'documents.document':
+ fname = os.path.join(target, element[document_exporter.EXPORTER_FILE_NAME])
+ self.assertTrue(os.path.exists(fname))
+ self.assertTrue(os.path.exists(os.path.join(target, element[document_exporter.EXPORTER_THUMBNAIL_NAME])))
+
+ with open(fname, "rb") as f:
+ checksum = hashlib.md5(f.read()).hexdigest()
+ self.assertEqual(checksum, element['fields']['checksum'])
+
+ if document_exporter.EXPORTER_ARCHIVE_NAME in element:
+ fname = os.path.join(target, element[document_exporter.EXPORTER_ARCHIVE_NAME])
+ self.assertTrue(os.path.exists(fname))
+
+ with open(fname, "rb") as f:
+ checksum = hashlib.md5(f.read()).hexdigest()
+ self.assertEqual(checksum, element['fields']['archive_checksum'])
+
+ with paperless_environment() as dirs:
+ call_command('document_importer', target)
+ messages = check_sanity()
+ # everything is alright after the test
+ self.assertEqual(len(messages), 0, str([str(m) for m in messages]))
+
+ def test_export_missing_files(self):
+
+ target = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, target)
+ Document.objects.create(checksum="AAAAAAAAAAAAAAAAA", title="wow", filename="0000004.pdf", mime_type="application/pdf")
+ self.assertRaises(FileNotFoundError, call_command, 'document_exporter', target)
diff --git a/src/documents/tests/test_management_retagger.py b/src/documents/tests/test_management_retagger.py
new file mode 100644
index 000000000..907a23d09
--- /dev/null
+++ b/src/documents/tests/test_management_retagger.py
@@ -0,0 +1,78 @@
+from django.core.management import call_command
+from django.test import TestCase
+
+from documents.models import Document, Tag, Correspondent, DocumentType
+from documents.tests.utils import DirectoriesMixin
+
+
+class TestRetagger(DirectoriesMixin, TestCase):
+
+ def make_models(self):
+ self.d1 = Document.objects.create(checksum="A", title="A", content="first document")
+ self.d2 = Document.objects.create(checksum="B", title="B", content="second document")
+ self.d3 = Document.objects.create(checksum="C", title="C", content="unrelated document")
+
+ self.tag_first = Tag.objects.create(name="tag1", match="first", matching_algorithm=Tag.MATCH_ANY)
+ self.tag_second = Tag.objects.create(name="tag2", match="second", matching_algorithm=Tag.MATCH_ANY)
+ self.tag_inbox = Tag.objects.create(name="test", is_inbox_tag=True)
+ self.tag_no_match = Tag.objects.create(name="test2")
+
+ self.d3.tags.add(self.tag_inbox)
+ self.d3.tags.add(self.tag_no_match)
+
+
+ self.correspondent_first = Correspondent.objects.create(
+ name="c1", match="first", matching_algorithm=Correspondent.MATCH_ANY)
+ self.correspondent_second = Correspondent.objects.create(
+ name="c2", match="second", matching_algorithm=Correspondent.MATCH_ANY)
+
+ self.doctype_first = DocumentType.objects.create(
+ name="dt1", match="first", matching_algorithm=DocumentType.MATCH_ANY)
+ self.doctype_second = DocumentType.objects.create(
+ name="dt2", match="second", matching_algorithm=DocumentType.MATCH_ANY)
+
+ def get_updated_docs(self):
+ return Document.objects.get(title="A"), Document.objects.get(title="B"), Document.objects.get(title="C")
+
+ def setUp(self) -> None:
+ super(TestRetagger, self).setUp()
+ self.make_models()
+
+ def test_add_tags(self):
+ call_command('document_retagger', '--tags')
+ d_first, d_second, d_unrelated = self.get_updated_docs()
+
+ self.assertEqual(d_first.tags.count(), 1)
+ self.assertEqual(d_second.tags.count(), 1)
+ self.assertEqual(d_unrelated.tags.count(), 2)
+
+ self.assertEqual(d_first.tags.first(), self.tag_first)
+ self.assertEqual(d_second.tags.first(), self.tag_second)
+
+ def test_add_type(self):
+ call_command('document_retagger', '--document_type')
+ d_first, d_second, d_unrelated = self.get_updated_docs()
+
+ self.assertEqual(d_first.document_type, self.doctype_first)
+ self.assertEqual(d_second.document_type, self.doctype_second)
+
+ def test_add_correspondent(self):
+ call_command('document_retagger', '--correspondent')
+ d_first, d_second, d_unrelated = self.get_updated_docs()
+
+ self.assertEqual(d_first.correspondent, self.correspondent_first)
+ self.assertEqual(d_second.correspondent, self.correspondent_second)
+
+ def test_overwrite_preserve_inbox(self):
+ self.d1.tags.add(self.tag_second)
+
+ call_command('document_retagger', '--tags', '--overwrite')
+
+ d_first, d_second, d_unrelated = self.get_updated_docs()
+
+ self.assertIsNotNone(Tag.objects.get(id=self.tag_second.id))
+
+ self.assertCountEqual([tag.id for tag in d_first.tags.all()], [self.tag_first.id])
+ self.assertCountEqual([tag.id for tag in d_second.tags.all()], [self.tag_second.id])
+ self.assertCountEqual([tag.id for tag in d_unrelated.tags.all()], [self.tag_inbox.id, self.tag_no_match.id])
+
diff --git a/src/documents/tests/test_matchables.py b/src/documents/tests/test_matchables.py
index e592237b6..4e4a3e7dc 100644
--- a/src/documents/tests/test_matchables.py
+++ b/src/documents/tests/test_matchables.py
@@ -1,17 +1,20 @@
+import shutil
+import tempfile
from random import randint
from django.contrib.admin.models import LogEntry
from django.contrib.auth.models import User
from django.test import TestCase, override_settings
-from ..models import Correspondent, Document, Tag
+from .. import matching
+from ..models import Correspondent, Document, Tag, DocumentType
from ..signals import document_consumption_finished
class TestMatching(TestCase):
def _test_matching(self, text, algorithm, true, false):
- for klass in (Tag, Correspondent):
+ for klass in (Tag, Correspondent, DocumentType):
instance = klass.objects.create(
name=str(randint(10000, 99999)),
match=text,
@@ -19,12 +22,12 @@ class TestMatching(TestCase):
)
for string in true:
self.assertTrue(
- instance.matches(string),
+ matching.matches(instance, string),
'"%s" should match "%s" but it does not' % (text, string)
)
for string in false:
self.assertFalse(
- instance.matches(string),
+ matching.matches(instance, string),
'"%s" should not match "%s" but it does' % (text, string)
)
@@ -212,7 +215,14 @@ class TestDocumentConsumptionFinishedSignal(TestCase):
TestCase.setUp(self)
User.objects.create_user(username='test_consumer', password='12345')
self.doc_contains = Document.objects.create(
- content="I contain the keyword.", file_type="pdf")
+ content="I contain the keyword.", mime_type="application/pdf")
+
+ self.index_dir = tempfile.mkdtemp()
+ # TODO: we should not need the index here.
+ override_settings(INDEX_DIR=self.index_dir).enable()
+
+ def tearDown(self) -> None:
+ shutil.rmtree(self.index_dir, ignore_errors=True)
def test_tag_applied_any(self):
t1 = Tag.objects.create(
diff --git a/src/documents/tests/test_models.py b/src/documents/tests/test_models.py
index 606403ec1..37b088c7f 100644
--- a/src/documents/tests/test_models.py
+++ b/src/documents/tests/test_models.py
@@ -1,7 +1,7 @@
from django.test import TestCase
-from ..models import Document, Correspondent
from .factories import DocumentFactory, CorrespondentFactory
+from ..models import Document, Correspondent
class CorrespondentTestCase(TestCase):
diff --git a/src/documents/tests/test_parsers.py b/src/documents/tests/test_parsers.py
new file mode 100644
index 000000000..805e4beac
--- /dev/null
+++ b/src/documents/tests/test_parsers.py
@@ -0,0 +1,122 @@
+import os
+import shutil
+import tempfile
+from tempfile import TemporaryDirectory
+from unittest import mock
+
+from django.test import TestCase, override_settings
+
+from documents.parsers import get_parser_class, get_supported_file_extensions, get_default_file_extension, \
+ get_parser_class_for_mime_type, DocumentParser, is_file_ext_supported
+from paperless_tesseract.parsers import RasterisedDocumentParser
+from paperless_text.parsers import TextDocumentParser
+
+
+def fake_magic_from_file(file, mime=False):
+
+ if mime:
+ if os.path.splitext(file)[1] == ".pdf":
+ return "application/pdf"
+ else:
+ return "unknown"
+ else:
+ return "A verbose string that describes the contents of the file"
+
+
+@mock.patch("documents.parsers.magic.from_file", fake_magic_from_file)
+class TestParserDiscovery(TestCase):
+
+ @mock.patch("documents.parsers.document_consumer_declaration.send")
+ def test__get_parser_class_1_parser(self, m, *args):
+ class DummyParser(object):
+ pass
+
+ m.return_value = (
+ (None, {"weight": 0, "parser": DummyParser, "mime_types": {"application/pdf": ".pdf"}}),
+ )
+
+ self.assertEqual(
+ get_parser_class("doc.pdf"),
+ DummyParser
+ )
+
+ @mock.patch("documents.parsers.document_consumer_declaration.send")
+ def test__get_parser_class_n_parsers(self, m, *args):
+
+ class DummyParser1(object):
+ pass
+
+ class DummyParser2(object):
+ pass
+
+ m.return_value = (
+ (None, {"weight": 0, "parser": DummyParser1, "mime_types": {"application/pdf": ".pdf"}}),
+ (None, {"weight": 1, "parser": DummyParser2, "mime_types": {"application/pdf": ".pdf"}}),
+ )
+
+ self.assertEqual(
+ get_parser_class("doc.pdf"),
+ DummyParser2
+ )
+
+ @mock.patch("documents.parsers.document_consumer_declaration.send")
+ def test__get_parser_class_0_parsers(self, m, *args):
+ m.return_value = []
+ with TemporaryDirectory() as tmpdir:
+ self.assertIsNone(
+ get_parser_class("doc.pdf")
+ )
+
+
+def fake_get_thumbnail(self, path, mimetype):
+ return os.path.join(os.path.dirname(__file__), "examples", "no-text.png")
+
+
+class TestBaseParser(TestCase):
+
+ def setUp(self) -> None:
+
+ self.scratch = tempfile.mkdtemp()
+ override_settings(
+ SCRATCH_DIR=self.scratch
+ ).enable()
+
+ def tearDown(self) -> None:
+ shutil.rmtree(self.scratch)
+
+ @mock.patch("documents.parsers.DocumentParser.get_thumbnail", fake_get_thumbnail)
+ @override_settings(OPTIMIZE_THUMBNAILS=True)
+ def test_get_optimised_thumbnail(self):
+ parser = DocumentParser(None)
+
+ parser.get_optimised_thumbnail("any", "not important")
+
+ @mock.patch("documents.parsers.DocumentParser.get_thumbnail", fake_get_thumbnail)
+ @override_settings(OPTIMIZE_THUMBNAILS=False)
+ def test_get_optimised_thumb_disabled(self):
+ parser = DocumentParser(None)
+
+ path = parser.get_optimised_thumbnail("any", "not important")
+ self.assertEqual(path, fake_get_thumbnail(None, None, None))
+
+
+class TestParserAvailability(TestCase):
+
+ def test_file_extensions(self):
+
+ for ext in [".pdf", ".jpe", ".jpg", ".jpeg", ".txt", ".csv"]:
+ self.assertIn(ext, get_supported_file_extensions())
+ self.assertEqual(get_default_file_extension('application/pdf'), ".pdf")
+ self.assertEqual(get_default_file_extension('image/png'), ".png")
+ self.assertEqual(get_default_file_extension('image/jpeg'), ".jpg")
+ self.assertEqual(get_default_file_extension('text/plain'), ".txt")
+ self.assertEqual(get_default_file_extension('text/csv'), ".csv")
+ self.assertEqual(get_default_file_extension('application/zip'), ".zip")
+ self.assertEqual(get_default_file_extension('aasdasd/dgfgf'), "")
+
+ self.assertEqual(get_parser_class_for_mime_type('application/pdf'), RasterisedDocumentParser)
+ self.assertEqual(get_parser_class_for_mime_type('text/plain'), TextDocumentParser)
+ self.assertEqual(get_parser_class_for_mime_type('text/sdgsdf'), None)
+
+ self.assertTrue(is_file_ext_supported('.pdf'))
+ self.assertFalse(is_file_ext_supported('.hsdfh'))
diff --git a/src/documents/tests/test_post_consume_handlers.py b/src/documents/tests/test_post_consume_handlers.py
new file mode 100644
index 000000000..b4357448c
--- /dev/null
+++ b/src/documents/tests/test_post_consume_handlers.py
@@ -0,0 +1,56 @@
+from unittest import mock
+
+from django.test import TestCase, override_settings
+
+from documents.models import Document, Tag, Correspondent
+from documents.signals.handlers import run_post_consume_script
+
+
+class PostConsumeTestCase(TestCase):
+
+ @mock.patch("documents.signals.handlers.Popen")
+ @override_settings(POST_CONSUME_SCRIPT=None)
+ def test_no_post_consume_script(self, m):
+ doc = Document.objects.create(title="Test", mime_type="application/pdf")
+ tag1 = Tag.objects.create(name="a")
+ tag2 = Tag.objects.create(name="b")
+ doc.tags.add(tag1)
+ doc.tags.add(tag2)
+
+ run_post_consume_script(None, doc)
+
+ m.assert_not_called()
+
+ @mock.patch("documents.signals.handlers.Popen")
+ @override_settings(POST_CONSUME_SCRIPT="script")
+ def test_post_consume_script_simple(self, m):
+ doc = Document.objects.create(title="Test", mime_type="application/pdf")
+
+ run_post_consume_script(None, doc)
+
+ m.assert_called_once()
+
+ @mock.patch("documents.signals.handlers.Popen")
+ @override_settings(POST_CONSUME_SCRIPT="script")
+ def test_post_consume_script_with_correspondent(self, m):
+ c = Correspondent.objects.create(name="my_bank")
+ doc = Document.objects.create(title="Test", mime_type="application/pdf", correspondent=c)
+ tag1 = Tag.objects.create(name="a")
+ tag2 = Tag.objects.create(name="b")
+ doc.tags.add(tag1)
+ doc.tags.add(tag2)
+
+ run_post_consume_script(None, doc)
+
+ m.assert_called_once()
+
+ args, kwargs = m.call_args
+
+ command = args[0]
+
+ self.assertEqual(command[0], "script")
+ self.assertEqual(command[1], str(doc.pk))
+ self.assertEqual(command[5], f"/api/documents/{doc.pk}/download/")
+ self.assertEqual(command[6], f"/api/documents/{doc.pk}/thumb/")
+ self.assertEqual(command[7], "my_bank")
+ self.assertCountEqual(command[8].split(","), ["a", "b"])
diff --git a/src/documents/tests/test_sanity_check.py b/src/documents/tests/test_sanity_check.py
new file mode 100644
index 000000000..0554cd7cd
--- /dev/null
+++ b/src/documents/tests/test_sanity_check.py
@@ -0,0 +1,91 @@
+import os
+import shutil
+from pathlib import Path
+
+import filelock
+from django.conf import settings
+from django.test import TestCase
+
+from documents.models import Document
+from documents.sanity_checker import check_sanity, SanityFailedError
+from documents.tests.utils import DirectoriesMixin
+
+
+class TestSanityCheck(DirectoriesMixin, TestCase):
+
+ def make_test_data(self):
+
+ with filelock.FileLock(settings.MEDIA_LOCK):
+ # just make sure that the lockfile is present.
+ shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "documents", "originals", "0000001.pdf"), os.path.join(self.dirs.originals_dir, "0000001.pdf"))
+ shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "documents", "archive", "0000001.pdf"), os.path.join(self.dirs.archive_dir, "0000001.pdf"))
+ shutil.copy(os.path.join(os.path.dirname(__file__), "samples", "documents", "thumbnails", "0000001.png"), os.path.join(self.dirs.thumbnail_dir, "0000001.png"))
+
+ return Document.objects.create(title="test", checksum="42995833e01aea9b3edee44bbfdd7ce1", archive_checksum="62acb0bcbfbcaa62ca6ad3668e4e404b", content="test", pk=1, filename="0000001.pdf", mime_type="application/pdf")
+
+ def test_no_docs(self):
+ self.assertEqual(len(check_sanity()), 0)
+
+ def test_success(self):
+ self.make_test_data()
+ self.assertEqual(len(check_sanity()), 0)
+
+ def test_no_thumbnail(self):
+ doc = self.make_test_data()
+ os.remove(doc.thumbnail_path)
+ self.assertEqual(len(check_sanity()), 1)
+
+ def test_thumbnail_no_access(self):
+ doc = self.make_test_data()
+ os.chmod(doc.thumbnail_path, 0o000)
+ self.assertEqual(len(check_sanity()), 1)
+ os.chmod(doc.thumbnail_path, 0o777)
+
+ def test_no_original(self):
+ doc = self.make_test_data()
+ os.remove(doc.source_path)
+ self.assertEqual(len(check_sanity()), 1)
+
+ def test_original_no_access(self):
+ doc = self.make_test_data()
+ os.chmod(doc.source_path, 0o000)
+ self.assertEqual(len(check_sanity()), 1)
+ os.chmod(doc.source_path, 0o777)
+
+ def test_original_checksum_mismatch(self):
+ doc = self.make_test_data()
+ doc.checksum = "WOW"
+ doc.save()
+ self.assertEqual(len(check_sanity()), 1)
+
+ def test_no_archive(self):
+ doc = self.make_test_data()
+ os.remove(doc.archive_path)
+ self.assertEqual(len(check_sanity()), 1)
+
+ def test_archive_no_access(self):
+ doc = self.make_test_data()
+ os.chmod(doc.archive_path, 0o000)
+ self.assertEqual(len(check_sanity()), 1)
+ os.chmod(doc.archive_path, 0o777)
+
+ def test_archive_checksum_mismatch(self):
+ doc = self.make_test_data()
+ doc.archive_checksum = "WOW"
+ doc.save()
+ self.assertEqual(len(check_sanity()), 1)
+
+ def test_empty_content(self):
+ doc = self.make_test_data()
+ doc.content = ""
+ doc.save()
+ self.assertEqual(len(check_sanity()), 1)
+
+ def test_orphaned_file(self):
+ doc = self.make_test_data()
+ Path(self.dirs.originals_dir, "orphaned").touch()
+ self.assertEqual(len(check_sanity()), 1)
+
+ def test_all(self):
+ Document.objects.create(title="test", checksum="dgfhj", archive_checksum="dfhg", content="", pk=1, filename="0000001.pdf")
+ string = str(SanityFailedError(check_sanity()))
diff --git a/src/documents/tests/test_tasks.py b/src/documents/tests/test_tasks.py
new file mode 100644
index 000000000..6d04e58e1
--- /dev/null
+++ b/src/documents/tests/test_tasks.py
@@ -0,0 +1,24 @@
+from datetime import datetime
+
+from django.test import TestCase
+from django.utils import timezone
+
+from documents import tasks
+from documents.models import Document
+from documents.tests.utils import DirectoriesMixin
+
+
+class TestTasks(DirectoriesMixin, TestCase):
+
+ def test_index_reindex(self):
+ Document.objects.create(title="test", content="my document", checksum="wow", added=timezone.now(), created=timezone.now(), modified=timezone.now())
+
+ tasks.index_reindex()
+
+ def test_index_optimize(self):
+ Document.objects.create(title="test", content="my document", checksum="wow", added=timezone.now(), created=timezone.now(), modified=timezone.now())
+
+ tasks.index_optimize()
+
+ def test_train_classifier(self):
+ tasks.train_classifier()
diff --git a/src/documents/tests/utils.py b/src/documents/tests/utils.py
new file mode 100644
index 000000000..dfefc4061
--- /dev/null
+++ b/src/documents/tests/utils.py
@@ -0,0 +1,77 @@
+import os
+import shutil
+import tempfile
+from collections import namedtuple
+from contextlib import contextmanager
+
+from django.test import override_settings
+
+
+def setup_directories():
+
+ dirs = namedtuple("Dirs", ())
+
+ dirs.data_dir = tempfile.mkdtemp()
+ dirs.scratch_dir = tempfile.mkdtemp()
+ dirs.media_dir = tempfile.mkdtemp()
+ dirs.consumption_dir = tempfile.mkdtemp()
+ dirs.index_dir = os.path.join(dirs.data_dir, "index")
+ dirs.originals_dir = os.path.join(dirs.media_dir, "documents", "originals")
+ dirs.thumbnail_dir = os.path.join(dirs.media_dir, "documents", "thumbnails")
+ dirs.archive_dir = os.path.join(dirs.media_dir, "documents", "archive")
+
+ os.makedirs(dirs.index_dir, exist_ok=True)
+ os.makedirs(dirs.originals_dir, exist_ok=True)
+ os.makedirs(dirs.thumbnail_dir, exist_ok=True)
+ os.makedirs(dirs.archive_dir, exist_ok=True)
+
+ dirs.settings_override = override_settings(
+ DATA_DIR=dirs.data_dir,
+ SCRATCH_DIR=dirs.scratch_dir,
+ MEDIA_ROOT=dirs.media_dir,
+ ORIGINALS_DIR=dirs.originals_dir,
+ THUMBNAIL_DIR=dirs.thumbnail_dir,
+ ARCHIVE_DIR=dirs.archive_dir,
+ CONSUMPTION_DIR=dirs.consumption_dir,
+ INDEX_DIR=dirs.index_dir,
+ MODEL_FILE=os.path.join(dirs.data_dir, "classification_model.pickle"),
+ MEDIA_LOCK=os.path.join(dirs.media_dir, "media.lock")
+
+ )
+ dirs.settings_override.enable()
+
+ return dirs
+
+
+def remove_dirs(dirs):
+ shutil.rmtree(dirs.media_dir, ignore_errors=True)
+ shutil.rmtree(dirs.data_dir, ignore_errors=True)
+ shutil.rmtree(dirs.scratch_dir, ignore_errors=True)
+ shutil.rmtree(dirs.consumption_dir, ignore_errors=True)
+ dirs.settings_override.disable()
+
+
+@contextmanager
+def paperless_environment():
+ dirs = None
+ try:
+ dirs = setup_directories()
+ yield dirs
+ finally:
+ if dirs:
+ remove_dirs(dirs)
+
+
+class DirectoriesMixin:
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.dirs = None
+
+ def setUp(self) -> None:
+ self.dirs = setup_directories()
+ super(DirectoriesMixin, self).setUp()
+
+ def tearDown(self) -> None:
+ super(DirectoriesMixin, self).tearDown()
+ remove_dirs(self.dirs)
diff --git a/src/documents/views.py b/src/documents/views.py
old mode 100644
new mode 100755
index aca849c70..bf31c749b
--- a/src/documents/views.py
+++ b/src/documents/views.py
@@ -1,12 +1,17 @@
-from django.http import HttpResponse, HttpResponseBadRequest
-from django.views.generic import DetailView, FormView, TemplateView
-from django_filters.rest_framework import DjangoFilterBackend
-from django.conf import settings
-from django.utils import cache
+import os
+import tempfile
+from datetime import datetime
+from time import mktime
-from paperless.db import GnuPG
-from paperless.mixins import SessionOrBasicAuthMixin
-from paperless.views import StandardPagination
+from django.conf import settings
+from django.db.models import Count, Max
+from django.http import HttpResponse, HttpResponseBadRequest, Http404
+from django.views.decorators.cache import cache_control
+from django.views.generic import TemplateView
+from django_filters.rest_framework import DjangoFilterBackend
+from django_q.tasks import async_task
+from rest_framework import parsers
+from rest_framework.decorators import action
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework.mixins import (
DestroyModelMixin,
@@ -15,111 +20,87 @@ from rest_framework.mixins import (
UpdateModelMixin
)
from rest_framework.permissions import IsAuthenticated
+from rest_framework.response import Response
+from rest_framework.views import APIView
from rest_framework.viewsets import (
GenericViewSet,
ModelViewSet,
ReadOnlyModelViewSet
)
-from .filters import CorrespondentFilterSet, DocumentFilterSet, TagFilterSet
-from .forms import UploadForm
-from .models import Correspondent, Document, Log, Tag
+import documents.index as index
+from paperless.db import GnuPG
+from paperless.views import StandardPagination
+from .filters import (
+ CorrespondentFilterSet,
+ DocumentFilterSet,
+ TagFilterSet,
+ DocumentTypeFilterSet,
+ LogFilterSet
+)
+from .models import Correspondent, Document, Log, Tag, DocumentType, SavedView
+from .parsers import get_parser_class_for_mime_type
from .serialisers import (
CorrespondentSerializer,
DocumentSerializer,
LogSerializer,
- TagSerializer
+ TagSerializer,
+ DocumentTypeSerializer,
+ PostDocumentSerializer,
+ SavedViewSerializer
)
class IndexView(TemplateView):
- template_name = "documents/index.html"
-
-
-class FetchView(SessionOrBasicAuthMixin, DetailView):
-
- model = Document
-
- def render_to_response(self, context, **response_kwargs):
- """
- Override the default to return the unencrypted image/PDF as raw data.
- """
-
- content_types = {
- Document.TYPE_PDF: "application/pdf",
- Document.TYPE_PNG: "image/png",
- Document.TYPE_JPG: "image/jpeg",
- Document.TYPE_GIF: "image/gif",
- Document.TYPE_TIF: "image/tiff",
- Document.TYPE_CSV: "text/csv",
- Document.TYPE_MD: "text/markdown",
- Document.TYPE_TXT: "text/plain"
- }
-
- if self.kwargs["kind"] == "thumb":
- response = HttpResponse(
- self._get_raw_data(self.object.thumbnail_file),
- content_type=content_types[Document.TYPE_PNG]
- )
- cache.patch_cache_control(response, max_age=31536000, private=True)
- return response
-
- response = HttpResponse(
- self._get_raw_data(self.object.source_file),
- content_type=content_types[self.object.file_type]
- )
-
- DISPOSITION = (
- 'inline' if settings.INLINE_DOC or self.kwargs["kind"] == 'preview'
- else 'attachment'
- )
-
- response["Content-Disposition"] = '{}; filename="{}"'.format(
- DISPOSITION, self.object.file_name)
-
- return response
-
- def _get_raw_data(self, file_handle):
- if self.object.storage_type == Document.STORAGE_TYPE_UNENCRYPTED:
- return file_handle
- return GnuPG.decrypted(file_handle)
-
-
-class PushView(SessionOrBasicAuthMixin, FormView):
- """
- A crude REST-ish API for creating documents.
- """
-
- form_class = UploadForm
-
- def form_valid(self, form):
- form.save()
- return HttpResponse("1", status=202)
-
- def form_invalid(self, form):
- return HttpResponseBadRequest(str(form.errors))
+ template_name = "index.html"
class CorrespondentViewSet(ModelViewSet):
model = Correspondent
- queryset = Correspondent.objects.all()
+
+ queryset = Correspondent.objects.annotate(
+ document_count=Count('documents'),
+ last_correspondence=Max('documents__created')).order_by('name')
+
serializer_class = CorrespondentSerializer
pagination_class = StandardPagination
permission_classes = (IsAuthenticated,)
filter_backends = (DjangoFilterBackend, OrderingFilter)
- filter_class = CorrespondentFilterSet
- ordering_fields = ("name", "slug")
+ filterset_class = CorrespondentFilterSet
+ ordering_fields = (
+ "name",
+ "matching_algorithm",
+ "match",
+ "document_count",
+ "last_correspondence")
class TagViewSet(ModelViewSet):
model = Tag
- queryset = Tag.objects.all()
+
+ queryset = Tag.objects.annotate(
+ document_count=Count('documents')).order_by('name')
+
serializer_class = TagSerializer
pagination_class = StandardPagination
permission_classes = (IsAuthenticated,)
filter_backends = (DjangoFilterBackend, OrderingFilter)
- filter_class = TagFilterSet
- ordering_fields = ("name", "slug")
+ filterset_class = TagFilterSet
+ ordering_fields = ("name", "matching_algorithm", "match", "document_count")
+
+
+class DocumentTypeViewSet(ModelViewSet):
+ model = DocumentType
+
+ queryset = DocumentType.objects.annotate(
+ document_count=Count('documents')).order_by('name')
+
+ serializer_class = DocumentTypeSerializer
+ pagination_class = StandardPagination
+ permission_classes = (IsAuthenticated,)
+ filter_backends = (DjangoFilterBackend, OrderingFilter)
+ filterset_class = DocumentTypeFilterSet
+ ordering_fields = ("name", "matching_algorithm", "match", "document_count")
class DocumentViewSet(RetrieveModelMixin,
@@ -133,17 +114,287 @@ class DocumentViewSet(RetrieveModelMixin,
pagination_class = StandardPagination
permission_classes = (IsAuthenticated,)
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
- filter_class = DocumentFilterSet
+ filterset_class = DocumentFilterSet
search_fields = ("title", "correspondent__name", "content")
ordering_fields = (
- "id", "title", "correspondent__name", "created", "modified", "added")
+ "id",
+ "title",
+ "correspondent__name",
+ "document_type__name",
+ "created",
+ "modified",
+ "added",
+ "archive_serial_number")
+
+ def update(self, request, *args, **kwargs):
+ response = super(DocumentViewSet, self).update(
+ request, *args, **kwargs)
+ index.add_or_update_document(self.get_object())
+ return response
+
+ def destroy(self, request, *args, **kwargs):
+ index.remove_document_from_index(self.get_object())
+ return super(DocumentViewSet, self).destroy(request, *args, **kwargs)
+
+ @staticmethod
+ def original_requested(request):
+ return (
+ 'original' in request.query_params and
+ request.query_params['original'] == 'true'
+ )
+
+ def file_response(self, pk, request, disposition):
+ doc = Document.objects.get(id=pk)
+ if not self.original_requested(request) and os.path.isfile(doc.archive_path): # NOQA: E501
+ file_handle = doc.archive_file
+ filename = doc.get_public_filename(archive=True)
+ mime_type = 'application/pdf'
+ else:
+ file_handle = doc.source_file
+ filename = doc.get_public_filename()
+ mime_type = doc.mime_type
+
+ if doc.storage_type == Document.STORAGE_TYPE_GPG:
+ file_handle = GnuPG.decrypted(file_handle)
+
+ response = HttpResponse(file_handle, content_type=mime_type)
+ response["Content-Disposition"] = '{}; filename="{}"'.format(
+ disposition, filename)
+ return response
+
+ def get_metadata(self, file, mime_type):
+ if not os.path.isfile(file):
+ return None
+
+ parser_class = get_parser_class_for_mime_type(mime_type)
+ if parser_class:
+ parser = parser_class(logging_group=None)
+
+ try:
+ return parser.extract_metadata(file, mime_type)
+ except Exception as e:
+ # TODO: cover GPG errors, remove later.
+ return []
+ else:
+ return []
+
+ @action(methods=['get'], detail=True)
+ def metadata(self, request, pk=None):
+ try:
+ doc = Document.objects.get(pk=pk)
+
+ meta = {
+ "original_checksum": doc.checksum,
+ "original_size": os.stat(doc.source_path).st_size,
+ "original_mime_type": doc.mime_type,
+ "media_filename": doc.filename,
+ "has_archive_version": os.path.isfile(doc.archive_path),
+ "original_metadata": self.get_metadata(
+ doc.source_path, doc.mime_type)
+ }
+
+ if doc.archive_checksum and os.path.isfile(doc.archive_path):
+ meta['archive_checksum'] = doc.archive_checksum
+ meta['archive_size'] = os.stat(doc.archive_path).st_size,
+ meta['archive_metadata'] = self.get_metadata(
+ doc.archive_path, "application/pdf")
+ else:
+ meta['archive_checksum'] = None
+ meta['archive_size'] = None
+ meta['archive_metadata'] = None
+
+ return Response(meta)
+ except Document.DoesNotExist:
+ raise Http404()
+
+ @action(methods=['get'], detail=True)
+ def preview(self, request, pk=None):
+ try:
+ response = self.file_response(
+ pk, request, "inline")
+ return response
+ except (FileNotFoundError, Document.DoesNotExist):
+ raise Http404()
+
+ @action(methods=['get'], detail=True)
+ @cache_control(public=False, max_age=315360000)
+ def thumb(self, request, pk=None):
+ try:
+ doc = Document.objects.get(id=pk)
+ if doc.storage_type == Document.STORAGE_TYPE_GPG:
+ handle = GnuPG.decrypted(doc.thumbnail_file)
+ else:
+ handle = doc.thumbnail_file
+ return HttpResponse(handle,
+ content_type='image/png')
+ except (FileNotFoundError, Document.DoesNotExist):
+ raise Http404()
+
+ @action(methods=['get'], detail=True)
+ def download(self, request, pk=None):
+ try:
+ return self.file_response(
+ pk, request, "attachment")
+ except (FileNotFoundError, Document.DoesNotExist):
+ raise Http404()
class LogViewSet(ReadOnlyModelViewSet):
model = Log
- queryset = Log.objects.all().by_group()
+
+ queryset = Log.objects.all()
serializer_class = LogSerializer
pagination_class = StandardPagination
permission_classes = (IsAuthenticated,)
filter_backends = (DjangoFilterBackend, OrderingFilter)
- ordering_fields = ("time",)
+ filterset_class = LogFilterSet
+ ordering_fields = ("created",)
+
+
+class SavedViewViewSet(ModelViewSet):
+ model = SavedView
+
+ queryset = SavedView.objects.all()
+ serializer_class = SavedViewSerializer
+ pagination_class = StandardPagination
+ permission_classes = (IsAuthenticated,)
+
+ def get_queryset(self):
+ user = self.request.user
+ return SavedView.objects.filter(user=user)
+
+ def perform_create(self, serializer):
+ serializer.save(user=self.request.user)
+
+
+class PostDocumentView(APIView):
+
+ permission_classes = (IsAuthenticated,)
+ serializer_class = PostDocumentSerializer
+ parser_classes = (parsers.MultiPartParser,)
+
+ def get_serializer_context(self):
+ return {
+ 'request': self.request,
+ 'format': self.format_kwarg,
+ 'view': self
+ }
+
+ def get_serializer(self, *args, **kwargs):
+ kwargs['context'] = self.get_serializer_context()
+ return self.serializer_class(*args, **kwargs)
+
+ def post(self, request, *args, **kwargs):
+
+ serializer = self.get_serializer(data=request.data)
+ serializer.is_valid(raise_exception=True)
+
+ doc_name, doc_data = serializer.validated_data.get('document')
+ correspondent_id = serializer.validated_data.get('correspondent')
+ document_type_id = serializer.validated_data.get('document_type')
+ tag_ids = serializer.validated_data.get('tags')
+ title = serializer.validated_data.get('title')
+
+ t = int(mktime(datetime.now().timetuple()))
+
+ os.makedirs(settings.SCRATCH_DIR, exist_ok=True)
+
+ with tempfile.NamedTemporaryFile(prefix="paperless-upload-",
+ dir=settings.SCRATCH_DIR,
+ delete=False) as f:
+ f.write(doc_data)
+ os.utime(f.name, times=(t, t))
+
+ async_task("documents.tasks.consume_file",
+ f.name,
+ override_filename=doc_name,
+ override_title=title,
+ override_correspondent_id=correspondent_id,
+ override_document_type_id=document_type_id,
+ override_tag_ids=tag_ids,
+ task_name=os.path.basename(doc_name)[:100])
+ return Response("OK")
+
+
+class SearchView(APIView):
+
+ permission_classes = (IsAuthenticated,)
+
+ def __init__(self, *args, **kwargs):
+ super(SearchView, self).__init__(*args, **kwargs)
+ self.ix = index.open_index()
+
+ def add_infos_to_hit(self, r):
+ doc = Document.objects.get(id=r['id'])
+ return {'id': r['id'],
+ 'highlights': r.highlights("content", text=doc.content),
+ 'score': r.score,
+ 'rank': r.rank,
+ 'document': DocumentSerializer(doc).data,
+ 'title': r['title']
+ }
+
+ def get(self, request, format=None):
+ if 'query' not in request.query_params:
+ return Response({
+ 'count': 0,
+ 'page': 0,
+ 'page_count': 0,
+ 'results': []})
+
+ query = request.query_params['query']
+ try:
+ page = int(request.query_params.get('page', 1))
+ except (ValueError, TypeError):
+ page = 1
+
+ if page < 1:
+ page = 1
+
+ try:
+ with index.query_page(self.ix, query, page) as (result_page,
+ corrected_query):
+ return Response(
+ {'count': len(result_page),
+ 'page': result_page.pagenum,
+ 'page_count': result_page.pagecount,
+ 'corrected_query': corrected_query,
+ 'results': list(map(self.add_infos_to_hit, result_page))})
+ except Exception as e:
+ return HttpResponseBadRequest(str(e))
+
+
+class SearchAutoCompleteView(APIView):
+
+ permission_classes = (IsAuthenticated,)
+
+ def __init__(self, *args, **kwargs):
+ super(SearchAutoCompleteView, self).__init__(*args, **kwargs)
+ self.ix = index.open_index()
+
+ def get(self, request, format=None):
+ if 'term' in request.query_params:
+ term = request.query_params['term']
+ else:
+ return HttpResponseBadRequest("Term required")
+
+ if 'limit' in request.query_params:
+ limit = int(request.query_params['limit'])
+ if limit <= 0:
+ return HttpResponseBadRequest("Invalid limit")
+ else:
+ limit = 10
+
+ return Response(index.autocomplete(self.ix, term, limit))
+
+
+class StatisticsView(APIView):
+
+ permission_classes = (IsAuthenticated,)
+
+ def get(self, request, format=None):
+ return Response({
+ 'documents_total': Document.objects.all().count(),
+ 'documents_inbox': Document.objects.filter(
+ tags__is_inbox_tag=True).distinct().count()
+ })
diff --git a/src/manage.py b/src/manage.py
old mode 100755
new mode 100644
diff --git a/src/paperless/auth.py b/src/paperless/auth.py
new file mode 100644
index 000000000..ece5d0eba
--- /dev/null
+++ b/src/paperless/auth.py
@@ -0,0 +1,28 @@
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.utils.deprecation import MiddlewareMixin
+from rest_framework import authentication
+
+
+class AutoLoginMiddleware(MiddlewareMixin):
+
+ def process_request(self, request):
+ try:
+ request.user = User.objects.get(
+ username=settings.AUTO_LOGIN_USERNAME)
+ except User.DoesNotExist:
+ pass
+
+
+class AngularApiAuthenticationOverride(authentication.BaseAuthentication):
+ """ This class is here to provide authentication to the angular dev server
+ during development. This is disabled in production.
+ """
+
+ def authenticate(self, request):
+ if settings.DEBUG and 'Referer' in request.headers and request.headers['Referer'].startswith('http://localhost:4200/'): # NOQA: E501
+ user = User.objects.filter(is_staff=True).first()
+ print("Auto-Login with user {}".format(user))
+ return (user, None)
+ else:
+ return None
diff --git a/src/paperless/checks.py b/src/paperless/checks.py
index e8c94362a..819582ffc 100644
--- a/src/paperless/checks.py
+++ b/src/paperless/checks.py
@@ -4,6 +4,31 @@ import shutil
from django.conf import settings
from django.core.checks import Error, Warning, register
+exists_message = "{} is set but doesn't exist."
+exists_hint = "Create a directory at {}"
+writeable_message = "{} is not writeable"
+writeable_hint = (
+ "Set the permissions of {} to be writeable by the user running the "
+ "Paperless services"
+)
+
+
+def path_check(env_var):
+ messages = []
+ directory = os.getenv(env_var)
+ if directory:
+ if not os.path.exists(directory):
+ messages.append(Error(
+ exists_message.format(env_var),
+ exists_hint.format(directory)
+ ))
+ elif not os.access(directory, os.W_OK | os.X_OK):
+ messages.append(Error(
+ writeable_message.format(env_var),
+ writeable_hint.format(directory)
+ ))
+ return messages
+
@register()
def paths_check(app_configs, **kwargs):
@@ -11,57 +36,10 @@ def paths_check(app_configs, **kwargs):
Check the various paths for existence, readability and writeability
"""
- check_messages = []
-
- exists_message = "{} is set but doesn't exist."
- exists_hint = "Create a directory at {}"
- writeable_message = "{} is not writeable"
- writeable_hint = (
- "Set the permissions of {} to be writeable by the user running the "
- "Paperless services"
- )
-
- directory = os.getenv("PAPERLESS_DBDIR")
- if directory:
- if not os.path.exists(directory):
- check_messages.append(Error(
- exists_message.format("PAPERLESS_DBDIR"),
- exists_hint.format(directory)
- ))
- if not check_messages:
- if not os.access(directory, os.W_OK | os.X_OK):
- check_messages.append(Error(
- writeable_message.format("PAPERLESS_DBDIR"),
- writeable_hint.format(directory)
- ))
-
- directory = os.getenv("PAPERLESS_MEDIADIR")
- if directory:
- if not os.path.exists(directory):
- check_messages.append(Error(
- exists_message.format("PAPERLESS_MEDIADIR"),
- exists_hint.format(directory)
- ))
- if not check_messages:
- if not os.access(directory, os.W_OK | os.X_OK):
- check_messages.append(Error(
- writeable_message.format("PAPERLESS_MEDIADIR"),
- writeable_hint.format(directory)
- ))
-
- directory = os.getenv("PAPERLESS_STATICDIR")
- if directory:
- if not os.path.exists(directory):
- check_messages.append(Error(
- exists_message.format("PAPERLESS_STATICDIR"),
- exists_hint.format(directory)
- ))
- if not check_messages:
- if not os.access(directory, os.W_OK | os.X_OK):
- check_messages.append(Error(
- writeable_message.format("PAPERLESS_STATICDIR"),
- writeable_hint.format(directory)
- ))
+ check_messages = path_check("PAPERLESS_DATA_DIR") + \
+ path_check("PAPERLESS_MEDIA_ROOT") + \
+ path_check("PAPERLESS_CONSUMPTION_DIR") + \
+ path_check("PAPERLESS_STATICDIR")
return check_messages
@@ -79,7 +57,6 @@ def binaries_check(app_configs, **kwargs):
binaries = (
settings.CONVERT_BINARY,
settings.OPTIPNG_BINARY,
- settings.UNPAPER_BINARY,
"tesseract"
)
@@ -89,3 +66,16 @@ def binaries_check(app_configs, **kwargs):
check_messages.append(Warning(error.format(binary), hint))
return check_messages
+
+
+@register()
+def debug_mode_check(app_configs, **kwargs):
+ if settings.DEBUG:
+ return [Warning(
+ "DEBUG mode is enabled. Disable Debug mode. This is a serious "
+ "security issue, since it puts security overides in place which "
+ "are meant to be only used during development. This "
+ "also means that paperless will tell anyone various "
+ "debugging information when something goes wrong.")]
+ else:
+ return []
diff --git a/src/paperless/db.py b/src/paperless/db.py
index 92275808b..5f476b5e7 100644
--- a/src/paperless/db.py
+++ b/src/paperless/db.py
@@ -17,16 +17,3 @@ class GnuPG:
passphrase = settings.PASSPHRASE
return cls.gpg.decrypt_file(file_handle, passphrase=passphrase).data
-
- @classmethod
- def encrypted(cls, file_handle, passphrase=None):
-
- if not passphrase:
- passphrase = settings.PASSPHRASE
-
- return cls.gpg.encrypt_file(
- file_handle,
- recipients=None,
- passphrase=passphrase,
- symmetric=True
- ).data
diff --git a/src/paperless/middleware.py b/src/paperless/middleware.py
deleted file mode 100644
index 58cdad65c..000000000
--- a/src/paperless/middleware.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from django.utils.deprecation import MiddlewareMixin
-from .models import User
-
-
-class Middleware(MiddlewareMixin):
- """
- This is a dummy authentication middleware class that creates what
- is roughly an Anonymous authenticated user so we can disable login
- and not interfere with existing user ID's. It's only used if
- login is disabled in paperless.conf (default is to require login)
- """
-
- def process_request(self, request):
- request.user = User()
diff --git a/src/paperless/mixins.py b/src/paperless/mixins.py
deleted file mode 100644
index f4f1fcdec..000000000
--- a/src/paperless/mixins.py
+++ /dev/null
@@ -1,46 +0,0 @@
-from django.contrib.auth.mixins import AccessMixin
-from django.contrib.auth import authenticate, login
-import base64
-
-
-class SessionOrBasicAuthMixin(AccessMixin):
- """
- Session or Basic Authentication mixin for Django.
- It determines if the requester is already logged in or if they have
- provided proper http-authorization and returning the view if all goes
- well, otherwise responding with a 401.
-
- Base for mixin found here: https://djangosnippets.org/snippets/3073/
- """
-
- def dispatch(self, request, *args, **kwargs):
-
- # check if user is authenticated via the session
- if request.user.is_authenticated:
-
- # Already logged in, just return the view.
- return super(SessionOrBasicAuthMixin, self).dispatch(
- request, *args, **kwargs
- )
-
- # apparently not authenticated via session, maybe via HTTP Basic?
- if 'HTTP_AUTHORIZATION' in request.META:
- auth = request.META['HTTP_AUTHORIZATION'].split()
- if len(auth) == 2:
- # NOTE: Support for only basic authentication
- if auth[0].lower() == "basic":
- authString = base64.b64decode(auth[1]).decode('utf-8')
- uname, passwd = authString.split(':')
- user = authenticate(username=uname, password=passwd)
- if user is not None:
- if user.is_active:
- login(request, user)
- request.user = user
- return super(
- SessionOrBasicAuthMixin, self
- ).dispatch(
- request, *args, **kwargs
- )
-
- # nope, really not authenticated
- return self.handle_no_permission()
diff --git a/src/paperless/models.py b/src/paperless/models.py
deleted file mode 100644
index e390032db..000000000
--- a/src/paperless/models.py
+++ /dev/null
@@ -1,31 +0,0 @@
-from django.contrib.auth.models import User as DjangoUser
-
-
-class User:
- """
- This is a dummy django User used with our middleware to disable
- login authentication if that is configured in paperless.conf
- """
-
- is_superuser = True
- is_active = True
- is_staff = True
- is_authenticated = True
-
- @property
- def id(self):
- return DjangoUser.objects.order_by("pk").first().pk
-
- @property
- def pk(self):
- return self.id
-
-
-"""
-NOTE: These are here as a hack instead of being in the User definition
-NOTE: above due to the way pycodestyle handles lamdbdas.
-NOTE: See https://github.com/PyCQA/pycodestyle/issues/379 for more.
-"""
-
-User.has_module_perms = lambda *_: True
-User.has_perm = lambda *_: True
diff --git a/src/paperless/settings.py b/src/paperless/settings.py
index 4667e62f5..1a6b80a0c 100644
--- a/src/paperless/settings.py
+++ b/src/paperless/settings.py
@@ -1,28 +1,31 @@
-"""
-Django settings for paperless project.
-
-Generated by 'django-admin startproject' using Django 1.9.
-
-For more information on this file, see
-https://docs.djangoproject.com/en/1.10/topics/settings/
-
-For the full list of settings and their values, see
-https://docs.djangoproject.com/en/1.10/ref/settings/
-"""
-
import json
+import math
+import multiprocessing
import os
import re
from dotenv import load_dotenv
-
# Tap paperless.conf if it's available
-if os.path.exists("/etc/paperless.conf"):
+if os.path.exists("../paperless.conf"):
+ load_dotenv("../paperless.conf")
+elif os.path.exists("/etc/paperless.conf"):
load_dotenv("/etc/paperless.conf")
elif os.path.exists("/usr/local/etc/paperless.conf"):
load_dotenv("/usr/local/etc/paperless.conf")
+# There are multiple levels of concurrency in paperless:
+# - Multiple consumers may be run in parallel.
+# - Each consumer may process multiple pages in parallel.
+# - Each Tesseract OCR run may spawn multiple threads to process a single page
+# slightly faster.
+# The performance gains from having tesseract use multiple threads are minimal.
+# However, when multiple pages are processed in parallel, the total number of
+# OCR threads may exceed the number of available cpu cores, which will
+# dramatically slow down the consumption process. This settings limits each
+# Tesseract process to one thread.
+os.environ['OMP_THREAD_LIMIT'] = "1"
+
def __get_boolean(key, default="NO"):
"""
@@ -32,36 +35,39 @@ def __get_boolean(key, default="NO"):
return bool(os.getenv(key, default).lower() in ("yes", "y", "1", "t", "true"))
-# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
+# NEVER RUN WITH DEBUG IN PRODUCTION.
+DEBUG = __get_boolean("PAPERLESS_DEBUG", "NO")
+
+
+###############################################################################
+# Directories #
+###############################################################################
+
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+STATIC_ROOT = os.getenv("PAPERLESS_STATICDIR", os.path.join(BASE_DIR, "..", "static"))
-# Quick-start development settings - unsuitable for production
-# See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
+MEDIA_ROOT = os.getenv('PAPERLESS_MEDIA_ROOT', os.path.join(BASE_DIR, "..", "media"))
+ORIGINALS_DIR = os.path.join(MEDIA_ROOT, "documents", "originals")
+ARCHIVE_DIR = os.path.join(MEDIA_ROOT, "documents", "archive")
+THUMBNAIL_DIR = os.path.join(MEDIA_ROOT, "documents", "thumbnails")
-# The secret key has a default that should be fine so long as you're hosting
-# Paperless on a closed network. However, if you're putting this anywhere
-# public, you should change the key to something unique and verbose.
-SECRET_KEY = os.getenv(
- "PAPERLESS_SECRET_KEY",
- "e11fl1oa-*ytql8p)(06fbj4ukrlo+n7k&q5+$1md7i+mge=ee"
-)
+DATA_DIR = os.getenv('PAPERLESS_DATA_DIR', os.path.join(BASE_DIR, "..", "data"))
+# Lock file for synchronizing changes to the MEDIA directory across multiple
+# threads.
+MEDIA_LOCK = os.path.join(MEDIA_ROOT, "media.lock")
+INDEX_DIR = os.path.join(DATA_DIR, "index")
+MODEL_FILE = os.path.join(DATA_DIR, "classification_model.pickle")
-# SECURITY WARNING: don't run with debug turned on in production!
-DEBUG = __get_boolean("PAPERLESS_DEBUG", "YES")
+CONSUMPTION_DIR = os.getenv("PAPERLESS_CONSUMPTION_DIR", os.path.join(BASE_DIR, "..", "consume"))
-LOGIN_URL = "admin:login"
+# This will be created if it doesn't exist
+SCRATCH_DIR = os.getenv("PAPERLESS_SCRATCH_DIR", "/tmp/paperless")
-ALLOWED_HOSTS = ["*"]
-
-_allowed_hosts = os.getenv("PAPERLESS_ALLOWED_HOSTS")
-if _allowed_hosts:
- ALLOWED_HOSTS = _allowed_hosts.split(",")
-
-FORCE_SCRIPT_NAME = os.getenv("PAPERLESS_FORCE_SCRIPT_NAME")
-
-# Application definition
+###############################################################################
+# Application Definition #
+###############################################################################
INSTALLED_APPS = [
"whitenoise.runserver_nostatic",
@@ -77,21 +83,32 @@ INSTALLED_APPS = [
"paperless",
"documents.apps.DocumentsConfig",
- "reminders.apps.RemindersConfig",
"paperless_tesseract.apps.PaperlessTesseractConfig",
"paperless_text.apps.PaperlessTextConfig",
+ "paperless_mail.apps.PaperlessMailConfig",
"django.contrib.admin",
"rest_framework",
- "crispy_forms",
+ "rest_framework.authtoken",
"django_filters",
- "djangoql",
+
+ "django_q",
]
-if os.getenv("PAPERLESS_INSTALLED_APPS"):
- INSTALLED_APPS += os.getenv("PAPERLESS_INSTALLED_APPS").split(",")
+REST_FRAMEWORK = {
+ 'DEFAULT_AUTHENTICATION_CLASSES': [
+ 'rest_framework.authentication.BasicAuthentication',
+ 'rest_framework.authentication.SessionAuthentication',
+ 'rest_framework.authentication.TokenAuthentication'
+ ]
+}
+
+if DEBUG:
+ REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'].append(
+ 'paperless.auth.AngularApiAuthenticationOverride'
+ )
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
@@ -105,19 +122,15 @@ MIDDLEWARE = [
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
-# Enable whitenoise compression and caching
-STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
-
-# We allow CORS from localhost:8080
-CORS_ORIGIN_WHITELIST = tuple(os.getenv("PAPERLESS_CORS_ALLOWED_HOSTS", "http://localhost:8080,https://localhost:8080").split(","))
-
-# If auth is disabled, we just use our "bypass" authentication middleware
-if bool(os.getenv("PAPERLESS_DISABLE_LOGIN", "false").lower() in ("yes", "y", "1", "t", "true")):
- _index = MIDDLEWARE.index("django.contrib.auth.middleware.AuthenticationMiddleware")
- MIDDLEWARE[_index] = "paperless.middleware.Middleware"
-
ROOT_URLCONF = 'paperless.urls'
+FORCE_SCRIPT_NAME = os.getenv("PAPERLESS_FORCE_SCRIPT_NAME")
+
+WSGI_APPLICATION = 'paperless.wsgi.application'
+
+STATIC_URL = os.getenv("PAPERLESS_STATIC_URL", "/static/")
+
+# what is this used for?
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
@@ -134,41 +147,46 @@ TEMPLATES = [
},
]
-WSGI_APPLICATION = 'paperless.wsgi.application'
+###############################################################################
+# Security #
+###############################################################################
+
+AUTO_LOGIN_USERNAME = os.getenv("PAPERLESS_AUTO_LOGIN_USERNAME")
+
+if AUTO_LOGIN_USERNAME:
+ _index = MIDDLEWARE.index('django.contrib.auth.middleware.AuthenticationMiddleware')
+ # This overrides everything the auth middleware is doing but still allows
+ # regular login in case the provided user does not exist.
+ MIDDLEWARE.insert(_index+1, 'paperless.auth.AutoLoginMiddleware')
-# Database
-# https://docs.djangoproject.com/en/1.10/ref/settings/#databases
+if DEBUG:
+ X_FRAME_OPTIONS = ''
+ # this should really be 'allow-from uri' but its not supported in any mayor
+ # browser.
+else:
+ X_FRAME_OPTIONS = 'SAMEORIGIN'
-DATABASES = {
- "default": {
- "ENGINE": "django.db.backends.sqlite3",
- "NAME": os.path.join(
- os.getenv(
- "PAPERLESS_DBDIR",
- os.path.join(BASE_DIR, "..", "data")
- ),
- "db.sqlite3"
- )
- }
-}
+# We allow CORS from localhost:8080
+CORS_ALLOWED_ORIGINS = tuple(os.getenv("PAPERLESS_CORS_ALLOWED_HOSTS", "http://localhost:8000").split(","))
-if os.getenv("PAPERLESS_DBUSER"):
- DATABASES["default"] = {
- "ENGINE": "django.db.backends.postgresql_psycopg2",
- "NAME": os.getenv("PAPERLESS_DBNAME", "paperless"),
- "USER": os.getenv("PAPERLESS_DBUSER"),
- }
- if os.getenv("PAPERLESS_DBPASS"):
- DATABASES["default"]["PASSWORD"] = os.getenv("PAPERLESS_DBPASS")
- if os.getenv("PAPERLESS_DBHOST"):
- DATABASES["default"]["HOST"] = os.getenv("PAPERLESS_DBHOST")
- if os.getenv("PAPERLESS_DBPORT"):
- DATABASES["default"]["PORT"] = os.getenv("PAPERLESS_DBPORT")
+if DEBUG:
+ # Allow access from the angular development server during debugging
+ CORS_ALLOWED_ORIGINS += ('http://localhost:4200',)
+# The secret key has a default that should be fine so long as you're hosting
+# Paperless on a closed network. However, if you're putting this anywhere
+# public, you should change the key to something unique and verbose.
+SECRET_KEY = os.getenv(
+ "PAPERLESS_SECRET_KEY",
+ "e11fl1oa-*ytql8p)(06fbj4ukrlo+n7k&q5+$1md7i+mge=ee"
+)
-# Password validation
-# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
+_allowed_hosts = os.getenv("PAPERLESS_ALLOWED_HOSTS")
+if _allowed_hosts:
+ ALLOWED_HOSTS = _allowed_hosts.split(",")
+else:
+ ALLOWED_HOSTS = ["*"]
AUTH_PASSWORD_VALIDATORS = [
{
@@ -185,9 +203,51 @@ AUTH_PASSWORD_VALIDATORS = [
},
]
+# Disable Django's artificial limit on the number of form fields to submit at
+# once. This is a protection against overloading the server, but since this is
+# a self-hosted sort of gig, the benefits of being able to mass-delete a tonne
+# of log entries outweight the benefits of such a safeguard.
-# Internationalization
-# https://docs.djangoproject.com/en/1.10/topics/i18n/
+DATA_UPLOAD_MAX_NUMBER_FIELDS = None
+
+COOKIE_PREFIX = os.getenv("PAPERLESS_COOKIE_PREFIX", "")
+
+CSRF_COOKIE_NAME = f"{COOKIE_PREFIX}csrftoken"
+SESSION_COOKIE_NAME = f"{COOKIE_PREFIX}sessionid"
+LANGUAGE_COOKIE_NAME = f"{COOKIE_PREFIX}django_language"
+
+###############################################################################
+# Database #
+###############################################################################
+
+DATABASES = {
+ "default": {
+ "ENGINE": "django.db.backends.sqlite3",
+ "NAME": os.path.join(
+ DATA_DIR,
+ "db.sqlite3"
+ )
+ }
+}
+
+if os.getenv("PAPERLESS_DBHOST"):
+ # Have sqlite available as a second option for management commands
+ # This is important when migrating to/from sqlite
+ DATABASES['sqlite'] = DATABASES['default'].copy()
+
+ DATABASES["default"] = {
+ "ENGINE": "django.db.backends.postgresql_psycopg2",
+ "HOST": os.getenv("PAPERLESS_DBHOST"),
+ "NAME": os.getenv("PAPERLESS_DBNAME", "paperless"),
+ "USER": os.getenv("PAPERLESS_DBUSER", "paperless"),
+ "PASSWORD": os.getenv("PAPERLESS_DBPASS", "paperless"),
+ }
+ if os.getenv("PAPERLESS_DBPORT"):
+ DATABASES["default"]["PORT"] = os.getenv("PAPERLESS_DBPORT")
+
+###############################################################################
+# Internationalization #
+###############################################################################
LANGUAGE_CODE = 'en-us'
@@ -199,67 +259,131 @@ USE_L10N = True
USE_TZ = True
+###############################################################################
+# Logging #
+###############################################################################
-# Static files (CSS, JavaScript, Images)
-# https://docs.djangoproject.com/en/1.10/howto/static-files/
-
-STATIC_ROOT = os.getenv(
- "PAPERLESS_STATICDIR", os.path.join(BASE_DIR, "..", "static"))
-MEDIA_ROOT = os.getenv(
- "PAPERLESS_MEDIADIR", os.path.join(BASE_DIR, "..", "media"))
-
-STATIC_URL = os.getenv("PAPERLESS_STATIC_URL", "/static/")
-MEDIA_URL = os.getenv("PAPERLESS_MEDIA_URL", "/media/")
-
-
-# Other
-
-# Disable Django's artificial limit on the number of form fields to submit at
-# once. This is a protection against overloading the server, but since this is
-# a self-hosted sort of gig, the benefits of being able to mass-delete a tonne
-# of log entries outweight the benefits of such a safeguard.
-
-DATA_UPLOAD_MAX_NUMBER_FIELDS = None
-
-
-# Paperless-specific stuff
-# You shouldn't have to edit any of these values. Rather, you can set these
-# values in /etc/paperless.conf instead.
-# ----------------------------------------------------------------------------
-
-# Logging
+DISABLE_DBHANDLER = __get_boolean("PAPERLESS_DISABLE_DBHANDLER")
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
+ 'formatters': {
+ 'verbose': {
+ 'format': '{levelname} {asctime} {module} {message}',
+ 'style': '{',
+ },
+ 'simple': {
+ 'format': '{levelname} {message}',
+ 'style': '{',
+ },
+ },
"handlers": {
- "consumer": {
- "class": "documents.loggers.PaperlessLogger",
+ "db": {
+ "level": "DEBUG",
+ "class": "documents.loggers.PaperlessHandler",
+ },
+ "console": {
+ "level": "INFO",
+ "class": "logging.StreamHandler",
+ "formatter": "verbose",
}
},
+ "root": {
+ "handlers": ["console"],
+ "level": "DEBUG",
+ },
"loggers": {
"documents": {
- "handlers": ["consumer"],
- "level": os.getenv("PAPERLESS_CONSUMER_LOG_LEVEL", "INFO"),
+ "handlers": ["db"],
+ "propagate": True,
+ },
+ "paperless_mail": {
+ "handlers": ["db"],
+ "propagate": True,
+ },
+ "paperless_tesseract": {
+ "handlers": ["db"],
+ "propagate": True,
},
},
}
+###############################################################################
+# Task queue #
+###############################################################################
+
+
+# Sensible defaults for multitasking:
+# use a fair balance between worker processes and threads epr worker so that
+# both consuming many documents in parallel and consuming large documents is
+# reasonably fast.
+# Favors threads per worker on smaller systems and never exceeds cpu_count()
+# in total.
+
+def default_task_workers():
+ try:
+ return max(
+ math.floor(math.sqrt(multiprocessing.cpu_count())),
+ 1
+ )
+ except NotImplementedError:
+ return 1
+
+
+TASK_WORKERS = int(os.getenv("PAPERLESS_TASK_WORKERS", default_task_workers()))
+
+Q_CLUSTER = {
+ 'name': 'paperless',
+ 'catch_up': False,
+ 'workers': TASK_WORKERS,
+ 'redis': os.getenv("PAPERLESS_REDIS", "redis://localhost:6379")
+}
+
+
+def default_threads_per_worker():
+ try:
+ return max(
+ math.floor(multiprocessing.cpu_count() / TASK_WORKERS),
+ 1
+ )
+ except NotImplementedError:
+ return 1
+
+
+THREADS_PER_WORKER = os.getenv("PAPERLESS_THREADS_PER_WORKER", default_threads_per_worker())
+
+###############################################################################
+# Paperless Specific Settings #
+###############################################################################
+
+CONSUMER_POLLING = int(os.getenv("PAPERLESS_CONSUMER_POLLING", 0))
+
+CONSUMER_DELETE_DUPLICATES = __get_boolean("PAPERLESS_CONSUMER_DELETE_DUPLICATES")
+
+CONSUMER_RECURSIVE = __get_boolean("PAPERLESS_CONSUMER_RECURSIVE")
+
+CONSUMER_SUBDIRS_AS_TAGS = __get_boolean("PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS")
+
+OPTIMIZE_THUMBNAILS = __get_boolean("PAPERLESS_OPTIMIZE_THUMBNAILS", "true")
+
+OCR_PAGES = int(os.getenv('PAPERLESS_OCR_PAGES', 0))
# The default language that tesseract will attempt to use when parsing
# documents. It should be a 3-letter language code consistent with ISO 639.
OCR_LANGUAGE = os.getenv("PAPERLESS_OCR_LANGUAGE", "eng")
-# The amount of threads to use for OCR
-OCR_THREADS = os.getenv("PAPERLESS_OCR_THREADS")
+# OCRmyPDF --output-type options are available.
+# TODO: validate this setting.
+OCR_OUTPUT_TYPE = os.getenv("PAPERLESS_OCR_OUTPUT_TYPE", "pdfa")
-# OCR all documents?
-OCR_ALWAYS = __get_boolean("PAPERLESS_OCR_ALWAYS")
+# skip. redo, force
+# TODO: validate this.
+OCR_MODE = os.getenv("PAPERLESS_OCR_MODE", "skip")
-# If this is true, any failed attempts to OCR a PDF will result in the PDF
-# being indexed anyway, with whatever we could get. If it's False, the file
-# will simply be left in the CONSUMPTION_DIR.
-FORGIVING_OCR = __get_boolean("PAPERLESS_FORGIVING_OCR")
+OCR_IMAGE_DPI = os.getenv("PAPERLESS_OCR_IMAGE_DPI")
+
+OCR_USER_ARGS = os.getenv("PAPERLESS_OCR_USER_ARGS", "{}")
# GNUPG needs a home directory for some reason
GNUPG_HOME = os.getenv("HOME", "/tmp")
@@ -268,29 +392,11 @@ GNUPG_HOME = os.getenv("HOME", "/tmp")
CONVERT_BINARY = os.getenv("PAPERLESS_CONVERT_BINARY", "convert")
CONVERT_TMPDIR = os.getenv("PAPERLESS_CONVERT_TMPDIR")
CONVERT_MEMORY_LIMIT = os.getenv("PAPERLESS_CONVERT_MEMORY_LIMIT")
-CONVERT_DENSITY = os.getenv("PAPERLESS_CONVERT_DENSITY")
-# Ghostscript
GS_BINARY = os.getenv("PAPERLESS_GS_BINARY", "gs")
-# OptiPNG
OPTIPNG_BINARY = os.getenv("PAPERLESS_OPTIPNG_BINARY", "optipng")
-# Unpaper
-UNPAPER_BINARY = os.getenv("PAPERLESS_UNPAPER_BINARY", "unpaper")
-
-# This will be created if it doesn't exist
-SCRATCH_DIR = os.getenv("PAPERLESS_SCRATCH_DIR", "/tmp/paperless")
-
-# This is where Paperless will look for PDFs to index
-CONSUMPTION_DIR = os.getenv("PAPERLESS_CONSUMPTION_DIR")
-
-# (This setting is ignored on Linux where inotify is used instead of a
-# polling loop.)
-# The number of seconds that Paperless will wait between checking
-# CONSUMPTION_DIR. If you tend to write documents to this directory very
-# slowly, you may want to use a higher value than the default.
-CONSUMER_LOOP_TIME = int(os.getenv("PAPERLESS_CONSUMER_LOOP_TIME", 10))
# Pre-2.x versions of Paperless stored your documents locally with GPG
# encryption, but that is no longer the default. This behaviour is still
@@ -309,17 +415,6 @@ PASSPHRASE = os.getenv("PAPERLESS_PASSPHRASE")
PRE_CONSUME_SCRIPT = os.getenv("PAPERLESS_PRE_CONSUME_SCRIPT")
POST_CONSUME_SCRIPT = os.getenv("PAPERLESS_POST_CONSUME_SCRIPT")
-# Whether to display a selected document inline, or download it as attachment:
-INLINE_DOC = __get_boolean("PAPERLESS_INLINE_DOC")
-
-# The number of items on each page in the web UI. This value must be a
-# positive integer, but if you don't define one in paperless.conf, a default of
-# 100 will be used.
-PAPERLESS_LIST_PER_PAGE = int(os.getenv("PAPERLESS_LIST_PER_PAGE", 100))
-
-FY_START = os.getenv("PAPERLESS_FINANCIAL_YEAR_START")
-FY_END = os.getenv("PAPERLESS_FINANCIAL_YEAR_END")
-
# Specify the default date order (for autodetected dates)
DATE_ORDER = os.getenv("PAPERLESS_DATE_ORDER", "DMY")
FILENAME_DATE_ORDER = os.getenv("PAPERLESS_FILENAME_DATE_ORDER")
@@ -329,11 +424,6 @@ FILENAME_PARSE_TRANSFORMS = []
for t in json.loads(os.getenv("PAPERLESS_FILENAME_PARSE_TRANSFORMS", "[]")):
FILENAME_PARSE_TRANSFORMS.append((re.compile(t["pattern"]), t["repl"]))
-# Specify for how many years a correspondent is considered recent. Recent
-# correspondents will be shown in a separate "Recent correspondents" filter as
-# well. Set to 0 to disable this filter.
-PAPERLESS_RECENT_CORRESPONDENT_YEARS = int(os.getenv(
- "PAPERLESS_RECENT_CORRESPONDENT_YEARS", 0))
-
+# TODO: this should not have a prefix.
# Specify the filename format for out files
PAPERLESS_FILENAME_FORMAT = os.getenv("PAPERLESS_FILENAME_FORMAT")
diff --git a/src/paperless/urls.py b/src/paperless/urls.py
old mode 100644
new mode 100755
index 4ab19b06f..079971bb3
--- a/src/paperless/urls.py
+++ b/src/paperless/urls.py
@@ -1,65 +1,97 @@
-from django.conf import settings
-from django.conf.urls import include, static, url
+from django.conf.urls import include
from django.contrib import admin
-from django.urls import reverse_lazy
+from django.contrib.auth.decorators import login_required
+from django.urls import path, re_path
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import RedirectView
+from rest_framework.authtoken import views
from rest_framework.routers import DefaultRouter
-from paperless.views import FaviconView
from documents.views import (
CorrespondentViewSet,
DocumentViewSet,
- FetchView,
LogViewSet,
- PushView,
- TagViewSet
+ TagViewSet,
+ DocumentTypeViewSet,
+ SearchView,
+ IndexView,
+ SearchAutoCompleteView,
+ StatisticsView,
+ PostDocumentView,
+ SavedViewViewSet
)
-from reminders.views import ReminderViewSet
+from paperless.views import FaviconView
+
+api_router = DefaultRouter()
+api_router.register(r"correspondents", CorrespondentViewSet)
+api_router.register(r"document_types", DocumentTypeViewSet)
+api_router.register(r"documents", DocumentViewSet)
+api_router.register(r"logs", LogViewSet)
+api_router.register(r"tags", TagViewSet)
+api_router.register(r"saved_views", SavedViewViewSet)
-router = DefaultRouter()
-router.register(r"correspondents", CorrespondentViewSet)
-router.register(r"documents", DocumentViewSet)
-router.register(r"logs", LogViewSet)
-router.register(r"reminders", ReminderViewSet)
-router.register(r"tags", TagViewSet)
urlpatterns = [
+ re_path(r"^api/", include([
+ re_path(r"^auth/",
+ include(('rest_framework.urls', 'rest_framework'),
+ namespace="rest_framework")),
- # API
- url(
- r"^api/auth/",
- include(
- ('rest_framework.urls', 'rest_framework'),
- namespace="rest_framework")
- ),
- url(r"^api/", include((router.urls, 'drf'), namespace="drf")),
+ re_path(r"^search/autocomplete/",
+ SearchAutoCompleteView.as_view(),
+ name="autocomplete"),
- # File downloads
- url(
- r"^fetch/(?Pdoc|thumb|preview)/(?P\d+)$",
- FetchView.as_view(),
- name="fetch"
- ),
+ re_path(r"^search/",
+ SearchView.as_view(),
+ name="search"),
- # File uploads
- url(r"^push$", csrf_exempt(PushView.as_view()), name="push"),
+ re_path(r"^statistics/",
+ StatisticsView.as_view(),
+ name="statistics"),
- # Favicon
- url(r"^favicon.ico$", FaviconView.as_view(), name="favicon"),
+ re_path(r"^documents/post_document/", PostDocumentView.as_view(),
+ name="post_document"),
- # The Django admin
- url(r"admin/", admin.site.urls),
+ path('token/', views.obtain_auth_token)
- # Redirect / to /admin
- url(r"^$", RedirectView.as_view(
- permanent=True, url=reverse_lazy("admin:index"))),
+ ] + api_router.urls)),
-] + static.static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
+ re_path(r"^favicon.ico$", FaviconView.as_view(), name="favicon"),
+
+ re_path(r"admin/", admin.site.urls),
+
+ re_path(r"^fetch/", include([
+ re_path(
+ r"^doc/(?P\d+)$",
+ RedirectView.as_view(url='/api/documents/%(pk)s/download/'),
+ ),
+ re_path(
+ r"^thumb/(?P\d+)$",
+ RedirectView.as_view(url='/api/documents/%(pk)s/thumb/'),
+ ),
+ re_path(
+ r"^preview/(?P\d+)$",
+ RedirectView.as_view(url='/api/documents/%(pk)s/preview/'),
+ ),
+ ])),
+
+ re_path(r"^push$", csrf_exempt(
+ RedirectView.as_view(url='/api/documents/post_document/'))),
+
+ # Frontend assets TODO: this is pretty bad, but it works.
+ path('assets/',
+ RedirectView.as_view(url='/static/frontend/assets/%(path)s')),
+
+ # login, logout
+ path('accounts/', include('django.contrib.auth.urls')),
+
+ # Root of the Frontent
+ re_path(r".*", login_required(IndexView.as_view())),
+]
# Text in each page's (and above login form).
-admin.site.site_header = 'Paperless'
+admin.site.site_header = 'Paperless-ng'
# Text at the end of each page's .
-admin.site.site_title = 'Paperless'
+admin.site.site_title = 'Paperless-ng'
# Text at the top of the admin index page.
-admin.site.index_title = 'Paperless administration'
+admin.site.index_title = 'Paperless-ng administration'
diff --git a/src/paperless/version.py b/src/paperless/version.py
index c2501767a..10283c145 100644
--- a/src/paperless/version.py
+++ b/src/paperless/version.py
@@ -1 +1 @@
-__version__ = (2, 6, 1)
+__version__ = (0, 9, 8)
diff --git a/src/paperless/views.py b/src/paperless/views.py
index b219379da..560a27980 100644
--- a/src/paperless/views.py
+++ b/src/paperless/views.py
@@ -7,7 +7,7 @@ from rest_framework.pagination import PageNumberPagination
class StandardPagination(PageNumberPagination):
page_size = 25
- page_size_query_param = "page-size"
+ page_size_query_param = "page_size"
max_page_size = 100000
diff --git a/src/paperless_mail/__init__.py b/src/paperless_mail/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/paperless_mail/admin.py b/src/paperless_mail/admin.py
new file mode 100644
index 000000000..0440234b7
--- /dev/null
+++ b/src/paperless_mail/admin.py
@@ -0,0 +1,70 @@
+from django.contrib import admin
+from paperless_mail.models import MailAccount, MailRule
+
+
+class MailAccountAdmin(admin.ModelAdmin):
+
+ list_display = ("name", "imap_server", "username")
+
+
+class MailRuleAdmin(admin.ModelAdmin):
+
+ radio_fields = {
+ "action": admin.VERTICAL,
+ "assign_title_from": admin.VERTICAL,
+ "assign_correspondent_from": admin.VERTICAL
+ }
+
+ fieldsets = (
+ (None, {
+ 'fields': ('name', 'order', 'account', 'folder')
+ }),
+ ("Filter", {
+ 'description':
+ "Paperless will only process mails that match ALL of the "
+ "filters given below.",
+ 'fields':
+ ('filter_from',
+ 'filter_subject',
+ 'filter_body',
+ 'maximum_age')
+ }),
+ ("Actions", {
+ 'description':
+ "The action applied to the mail. This action is only "
+ "performed when documents were consumed from the mail. Mails "
+ "without attachments will remain entirely untouched.",
+ 'fields': (
+ 'action',
+ 'action_parameter')
+ }),
+ ("Metadata", {
+ 'description':
+ "Assign metadata to documents consumed from this rule "
+ "automatically. If you do not assign tags, types or "
+ "correspondents here, paperless will still process all "
+ "matching rules that you have defined.",
+ "fields": (
+ 'assign_title_from',
+ 'assign_tag',
+ 'assign_document_type',
+ 'assign_correspondent_from',
+ 'assign_correspondent')
+ })
+ )
+
+ list_filter = ("account",)
+
+ list_display = ("order", "name", "account", "folder", "action")
+
+ list_editable = ("order", )
+
+ list_display_links = ("name", )
+
+ sortable_by = []
+
+ ordering = ["order"]
+
+
+admin.site.register(MailAccount, MailAccountAdmin)
+admin.site.register(MailRule, MailRuleAdmin)
diff --git a/src/paperless_mail/apps.py b/src/paperless_mail/apps.py
new file mode 100644
index 000000000..18c15a4f4
--- /dev/null
+++ b/src/paperless_mail/apps.py
@@ -0,0 +1,7 @@
+from django.apps import AppConfig
+
+
+class PaperlessMailConfig(AppConfig):
+ name = 'paperless_mail'
+
+ verbose_name = 'Paperless Mail'
diff --git a/src/paperless_mail/mail.py b/src/paperless_mail/mail.py
new file mode 100644
index 000000000..3c200362d
--- /dev/null
+++ b/src/paperless_mail/mail.py
@@ -0,0 +1,314 @@
+import os
+import tempfile
+from datetime import timedelta, date
+
+import magic
+from django.conf import settings
+from django.db import DatabaseError
+from django.utils.text import slugify
+from django_q.tasks import async_task
+from imap_tools import MailBox, MailBoxUnencrypted, AND, MailMessageFlags, \
+ MailboxFolderSelectError
+
+from documents.loggers import LoggingMixin
+from documents.models import Correspondent
+from documents.parsers import is_mime_type_supported
+from paperless_mail.models import MailAccount, MailRule
+
+
+class MailError(Exception):
+ pass
+
+
+class BaseMailAction:
+
+ def get_criteria(self):
+ return {}
+
+ def post_consume(self, M, message_uids, parameter):
+ pass # pragma: nocover
+
+
+class DeleteMailAction(BaseMailAction):
+
+ def post_consume(self, M, message_uids, parameter):
+ M.delete(message_uids)
+
+
+class MarkReadMailAction(BaseMailAction):
+
+ def get_criteria(self):
+ return {'seen': False}
+
+ def post_consume(self, M, message_uids, parameter):
+ M.seen(message_uids, True)
+
+
+class MoveMailAction(BaseMailAction):
+
+ def post_consume(self, M, message_uids, parameter):
+ M.move(message_uids, parameter)
+
+
+class FlagMailAction(BaseMailAction):
+
+ def get_criteria(self):
+ return {'flagged': False}
+
+ def post_consume(self, M, message_uids, parameter):
+ M.flag(message_uids, [MailMessageFlags.FLAGGED], True)
+
+
+def get_rule_action(rule):
+ if rule.action == MailRule.ACTION_FLAG:
+ return FlagMailAction()
+ elif rule.action == MailRule.ACTION_DELETE:
+ return DeleteMailAction()
+ elif rule.action == MailRule.ACTION_MOVE:
+ return MoveMailAction()
+ elif rule.action == MailRule.ACTION_MARK_READ:
+ return MarkReadMailAction()
+ else:
+ raise NotImplementedError("Unknown action.") # pragma: nocover
+
+
+def make_criterias(rule):
+ maximum_age = date.today() - timedelta(days=rule.maximum_age)
+ criterias = {
+ "date_gte": maximum_age
+ }
+ if rule.filter_from:
+ criterias["from_"] = rule.filter_from
+ if rule.filter_subject:
+ criterias["subject"] = rule.filter_subject
+ if rule.filter_body:
+ criterias["body"] = rule.filter_body
+
+ return {**criterias, **get_rule_action(rule).get_criteria()}
+
+
+def get_mailbox(server, port, security):
+ if security == MailAccount.IMAP_SECURITY_NONE:
+ mailbox = MailBoxUnencrypted(server, port)
+ elif security == MailAccount.IMAP_SECURITY_STARTTLS:
+ mailbox = MailBox(server, port, starttls=True)
+ elif security == MailAccount.IMAP_SECURITY_SSL:
+ mailbox = MailBox(server, port)
+ else:
+ raise NotImplementedError("Unknown IMAP security") # pragma: nocover
+ return mailbox
+
+
+class MailAccountHandler(LoggingMixin):
+
+ def _correspondent_from_name(self, name):
+ try:
+ return Correspondent.objects.get_or_create(name=name)[0]
+ except DatabaseError as e:
+ self.log(
+ "error",
+ f"Error while retrieving correspondent {name}: {e}"
+ )
+ return None
+
+ def get_title(self, message, att, rule):
+ if rule.assign_title_from == MailRule.TITLE_FROM_SUBJECT:
+ return message.subject
+
+ elif rule.assign_title_from == MailRule.TITLE_FROM_FILENAME:
+ return os.path.splitext(os.path.basename(att.filename))[0]
+
+ else:
+ raise NotImplementedError("Unknown title selector.") # pragma: nocover # NOQA: E501
+
+ def get_correspondent(self, message, rule):
+ c_from = rule.assign_correspondent_from
+
+ if c_from == MailRule.CORRESPONDENT_FROM_NOTHING:
+ return None
+
+ elif c_from == MailRule.CORRESPONDENT_FROM_EMAIL:
+ return self._correspondent_from_name(message.from_)
+
+ elif c_from == MailRule.CORRESPONDENT_FROM_NAME:
+ if message.from_values and 'name' in message.from_values and message.from_values['name']: # NOQA: E501
+ return self._correspondent_from_name(
+ message.from_values['name'])
+ else:
+ return self._correspondent_from_name(message.from_)
+
+ elif c_from == MailRule.CORRESPONDENT_FROM_CUSTOM:
+ return rule.assign_correspondent
+
+ else:
+ raise NotImplementedError("Unknwown correspondent selector") # pragma: nocover # NOQA: E501
+
+ def handle_mail_account(self, account):
+
+ self.renew_logging_group()
+
+ self.log('debug', f"Processing mail account {account}")
+
+ total_processed_files = 0
+
+ with get_mailbox(account.imap_server,
+ account.imap_port,
+ account.imap_security) as M:
+
+ try:
+ M.login(account.username, account.password)
+ except Exception:
+ raise MailError(
+ f"Error while authenticating account {account}")
+
+ self.log('debug', f"Account {account}: Processing "
+ f"{account.rules.count()} rule(s)")
+
+ for rule in account.rules.order_by('order'):
+ try:
+ total_processed_files += self.handle_mail_rule(M, rule)
+ except Exception as e:
+ self.log(
+ "error",
+ f"Rule {rule}: Error while processing rule: {e}",
+ exc_info=True
+ )
+
+ return total_processed_files
+
+ def handle_mail_rule(self, M, rule):
+
+ self.log(
+ 'debug',
+ f"Rule {rule}: Selecting folder {rule.folder}")
+
+ try:
+ M.folder.set(rule.folder)
+ except MailboxFolderSelectError:
+ raise MailError(
+ f"Rule {rule}: Folder {rule.folder} "
+ f"does not exist in account {rule.account}")
+
+ criterias = make_criterias(rule)
+
+ self.log(
+ 'debug',
+ f"Rule {rule}: Searching folder with criteria "
+ f"{str(AND(**criterias))}")
+
+ try:
+ messages = M.fetch(criteria=AND(**criterias),
+ mark_seen=False)
+ except Exception:
+ raise MailError(
+ f"Rule {rule}: Error while fetching folder {rule.folder}")
+
+ post_consume_messages = []
+
+ mails_processed = 0
+ total_processed_files = 0
+
+ for message in messages:
+ try:
+ processed_files = self.handle_message(message, rule)
+ if processed_files > 0:
+ post_consume_messages.append(message.uid)
+
+ total_processed_files += processed_files
+ mails_processed += 1
+ except Exception as e:
+ self.log(
+ "error",
+ f"Rule {rule}: Error while processing mail "
+ f"{message.uid}: {e}",
+ exc_info=True)
+
+ self.log(
+ 'debug',
+ f"Rule {rule}: Processed {mails_processed} matching mail(s)")
+
+ self.log(
+ 'debug',
+ f"Rule {rule}: Running mail actions on "
+ f"{len(post_consume_messages)} mails")
+
+ try:
+ get_rule_action(rule).post_consume(
+ M,
+ post_consume_messages,
+ rule.action_parameter)
+
+ except Exception as e:
+ raise MailError(
+ f"Rule {rule}: Error while processing post-consume actions: "
+ f"{e}")
+
+ return total_processed_files
+
+ def handle_message(self, message, rule):
+ if not message.attachments:
+ return 0
+
+ self.log(
+ 'debug',
+ f"Rule {rule}: "
+ f"Processing mail {message.subject} from {message.from_} with "
+ f"{len(message.attachments)} attachment(s)")
+
+ correspondent = self.get_correspondent(message, rule)
+ tag = rule.assign_tag
+ doc_type = rule.assign_document_type
+
+ processed_attachments = 0
+
+ for att in message.attachments:
+
+ if not att.content_disposition == "attachment":
+ self.log(
+ 'debug',
+ f"Rule {rule}: "
+ f"Skipping attachment {att.filename} "
+ f"with content disposition {att.content_disposition}")
+ continue
+
+ title = self.get_title(message, att, rule)
+
+ # don't trust the content type of the attachment. Could be
+ # generic application/octet-stream.
+ mime_type = magic.from_buffer(att.payload, mime=True)
+
+ if is_mime_type_supported(mime_type):
+
+ os.makedirs(settings.SCRATCH_DIR, exist_ok=True)
+ _, temp_filename = tempfile.mkstemp(prefix="paperless-mail-",
+ dir=settings.SCRATCH_DIR)
+ with open(temp_filename, 'wb') as f:
+ f.write(att.payload)
+
+ self.log(
+ 'info',
+ f"Rule {rule}: "
+ f"Consuming attachment {att.filename} from mail "
+ f"{message.subject} from {message.from_}")
+
+ async_task(
+ "documents.tasks.consume_file",
+ path=temp_filename,
+ override_filename=att.filename,
+ override_title=title,
+ override_correspondent_id=correspondent.id if correspondent else None, # NOQA: E501
+ override_document_type_id=doc_type.id if doc_type else None, # NOQA: E501
+ override_tag_ids=[tag.id] if tag else None,
+ task_name=att.filename[:100]
+ )
+
+ processed_attachments += 1
+ else:
+ self.log(
+ 'debug',
+ f"Rule {rule}: "
+ f"Skipping attachment {att.filename} "
+ f"since guessed mime type {mime_type} is not supported "
+ f"by paperless")
+
+ return processed_attachments
diff --git a/src/paperless_mail/management/__init__.py b/src/paperless_mail/management/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/paperless_mail/management/commands/__init__.py b/src/paperless_mail/management/commands/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/paperless_mail/management/commands/mail_fetcher.py b/src/paperless_mail/management/commands/mail_fetcher.py
new file mode 100644
index 000000000..b11b5b70d
--- /dev/null
+++ b/src/paperless_mail/management/commands/mail_fetcher.py
@@ -0,0 +1,13 @@
+from django.core.management.base import BaseCommand
+
+from paperless_mail import tasks
+
+
+class Command(BaseCommand):
+
+ help = """
+ """.replace(" ", "")
+
+ def handle(self, *args, **options):
+
+ tasks.process_mail_accounts()
diff --git a/src/paperless_mail/migrations/0001_initial.py b/src/paperless_mail/migrations/0001_initial.py
new file mode 100644
index 000000000..bb6328c60
--- /dev/null
+++ b/src/paperless_mail/migrations/0001_initial.py
@@ -0,0 +1,48 @@
+# Generated by Django 3.1.3 on 2020-11-15 22:54
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('documents', '1002_auto_20201111_1105'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='MailAccount',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=256, unique=True)),
+ ('imap_server', models.CharField(max_length=256)),
+ ('imap_port', models.IntegerField(blank=True, null=True)),
+ ('imap_security', models.PositiveIntegerField(choices=[(1, 'No encryption'), (2, 'Use SSL'), (3, 'Use STARTTLS')], default=2)),
+ ('username', models.CharField(max_length=256)),
+ ('password', models.CharField(max_length=256)),
+ ],
+ ),
+ migrations.CreateModel(
+ name='MailRule',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=256)),
+ ('folder', models.CharField(default='INBOX', max_length=256)),
+ ('filter_from', models.CharField(blank=True, max_length=256, null=True)),
+ ('filter_subject', models.CharField(blank=True, max_length=256, null=True)),
+ ('filter_body', models.CharField(blank=True, max_length=256, null=True)),
+ ('maximum_age', models.PositiveIntegerField(default=30)),
+ ('action', models.PositiveIntegerField(choices=[(1, 'Delete'), (2, 'Move to specified folder'), (3, "Mark as read, don't process read mails"), (4, "Flag the mail, don't process flagged mails")], default=3, help_text='The action applied to the mail. This action is only performed when documents were consumed from the mail. Mails without attachments will remain entirely untouched.')),
+ ('action_parameter', models.CharField(blank=True, help_text='Additional parameter for the action selected above, i.e., the target folder of the move to folder action.', max_length=256, null=True)),
+ ('assign_title_from', models.PositiveIntegerField(choices=[(1, 'Use subject as title'), (2, 'Use attachment filename as title')], default=1)),
+ ('assign_correspondent_from', models.PositiveIntegerField(choices=[(1, 'Do not assign a correspondent'), (2, 'Use mail address'), (3, 'Use name (or mail address if not available)'), (4, 'Use correspondent selected below')], default=1)),
+ ('account', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rules', to='paperless_mail.mailaccount')),
+ ('assign_correspondent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='documents.correspondent')),
+ ('assign_document_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='documents.documenttype')),
+ ('assign_tag', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='documents.tag')),
+ ],
+ ),
+ ]
diff --git a/src/paperless_mail/migrations/0002_auto_20201117_1334.py b/src/paperless_mail/migrations/0002_auto_20201117_1334.py
new file mode 100644
index 000000000..7a29ba248
--- /dev/null
+++ b/src/paperless_mail/migrations/0002_auto_20201117_1334.py
@@ -0,0 +1,32 @@
+# Generated by Django 3.1.3 on 2020-11-17 13:34
+
+from django.db import migrations
+from django.db.migrations import RunPython
+from django_q.models import Schedule
+from django_q.tasks import schedule
+
+
+def add_schedules(apps, schema_editor):
+ schedule('paperless_mail.tasks.process_mail_accounts',
+ name="Check all e-mail accounts",
+ schedule_type=Schedule.MINUTES,
+ minutes=10)
+
+
+def remove_schedules(apps, schema_editor):
+ Schedule.objects.filter(
+ func='paperless_mail.tasks.process_mail_accounts').delete()
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('paperless_mail', '0001_initial'),
+ ('django_q', '0013_task_attempt_count'),
+ ]
+
+ operations = [
+ RunPython(add_schedules, remove_schedules)
+ ]
+
+
diff --git a/src/paperless_mail/migrations/0003_auto_20201118_1940.py b/src/paperless_mail/migrations/0003_auto_20201118_1940.py
new file mode 100644
index 000000000..3339a6d7f
--- /dev/null
+++ b/src/paperless_mail/migrations/0003_auto_20201118_1940.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.1.3 on 2020-11-18 19:40
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('paperless_mail', '0002_auto_20201117_1334'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='mailaccount',
+ name='imap_port',
+ field=models.IntegerField(blank=True, help_text='This is usually 143 for unencrypted and STARTTLS connections, and 993 for SSL connections.', null=True),
+ ),
+ migrations.AlterField(
+ model_name='mailrule',
+ name='name',
+ field=models.CharField(max_length=256, unique=True),
+ ),
+ ]
diff --git a/src/paperless_mail/migrations/0004_mailrule_order.py b/src/paperless_mail/migrations/0004_mailrule_order.py
new file mode 100644
index 000000000..498f280a1
--- /dev/null
+++ b/src/paperless_mail/migrations/0004_mailrule_order.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.1.3 on 2020-11-21 21:51
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('paperless_mail', '0003_auto_20201118_1940'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='mailrule',
+ name='order',
+ field=models.IntegerField(default=0),
+ ),
+ ]
diff --git a/src/paperless_mail/migrations/0005_help_texts.py b/src/paperless_mail/migrations/0005_help_texts.py
new file mode 100644
index 000000000..71899c8ef
--- /dev/null
+++ b/src/paperless_mail/migrations/0005_help_texts.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.1.3 on 2020-11-22 10:36
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('paperless_mail', '0004_mailrule_order'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='mailrule',
+ name='action',
+ field=models.PositiveIntegerField(choices=[(3, "Mark as read, don't process read mails"), (4, "Flag the mail, don't process flagged mails"), (2, 'Move to specified folder'), (1, 'Delete')], default=3),
+ ),
+ migrations.AlterField(
+ model_name='mailrule',
+ name='maximum_age',
+ field=models.PositiveIntegerField(default=30, help_text='Specified in days.'),
+ ),
+ ]
diff --git a/src/paperless_mail/migrations/__init__.py b/src/paperless_mail/migrations/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/paperless_mail/models.py b/src/paperless_mail/models.py
new file mode 100644
index 000000000..aa1ac5684
--- /dev/null
+++ b/src/paperless_mail/models.py
@@ -0,0 +1,142 @@
+from django.db import models
+
+import documents.models as document_models
+
+
+class MailAccount(models.Model):
+
+ IMAP_SECURITY_NONE = 1
+ IMAP_SECURITY_SSL = 2
+ IMAP_SECURITY_STARTTLS = 3
+
+ IMAP_SECURITY_OPTIONS = (
+ (IMAP_SECURITY_NONE, "No encryption"),
+ (IMAP_SECURITY_SSL, "Use SSL"),
+ (IMAP_SECURITY_STARTTLS, "Use STARTTLS"),
+ )
+
+ name = models.CharField(max_length=256, unique=True)
+
+ imap_server = models.CharField(max_length=256)
+
+ imap_port = models.IntegerField(
+ blank=True,
+ null=True,
+ help_text="This is usually 143 for unencrypted and STARTTLS "
+ "connections, and 993 for SSL connections.")
+
+ imap_security = models.PositiveIntegerField(
+ choices=IMAP_SECURITY_OPTIONS,
+ default=IMAP_SECURITY_SSL
+ )
+
+ username = models.CharField(max_length=256)
+
+ password = models.CharField(max_length=256)
+
+ def __str__(self):
+ return self.name
+
+
+class MailRule(models.Model):
+
+ ACTION_DELETE = 1
+ ACTION_MOVE = 2
+ ACTION_MARK_READ = 3
+ ACTION_FLAG = 4
+
+ ACTIONS = (
+ (ACTION_MARK_READ, "Mark as read, don't process read mails"),
+ (ACTION_FLAG, "Flag the mail, don't process flagged mails"),
+ (ACTION_MOVE, "Move to specified folder"),
+ (ACTION_DELETE, "Delete"),
+ )
+
+ TITLE_FROM_SUBJECT = 1
+ TITLE_FROM_FILENAME = 2
+
+ TITLE_SELECTOR = (
+ (TITLE_FROM_SUBJECT, "Use subject as title"),
+ (TITLE_FROM_FILENAME, "Use attachment filename as title")
+ )
+
+ CORRESPONDENT_FROM_NOTHING = 1
+ CORRESPONDENT_FROM_EMAIL = 2
+ CORRESPONDENT_FROM_NAME = 3
+ CORRESPONDENT_FROM_CUSTOM = 4
+
+ CORRESPONDENT_SELECTOR = (
+ (CORRESPONDENT_FROM_NOTHING,
+ "Do not assign a correspondent"),
+ (CORRESPONDENT_FROM_EMAIL,
+ "Use mail address"),
+ (CORRESPONDENT_FROM_NAME,
+ "Use name (or mail address if not available)"),
+ (CORRESPONDENT_FROM_CUSTOM,
+ "Use correspondent selected below")
+ )
+
+ name = models.CharField(max_length=256, unique=True)
+
+ order = models.IntegerField(default=0)
+
+ account = models.ForeignKey(
+ MailAccount,
+ related_name="rules",
+ on_delete=models.CASCADE
+ )
+
+ folder = models.CharField(default='INBOX', max_length=256)
+
+ filter_from = models.CharField(max_length=256, null=True, blank=True)
+ filter_subject = models.CharField(max_length=256, null=True, blank=True)
+ filter_body = models.CharField(max_length=256, null=True, blank=True)
+
+ maximum_age = models.PositiveIntegerField(
+ default=30,
+ help_text="Specified in days.")
+
+ action = models.PositiveIntegerField(
+ choices=ACTIONS,
+ default=ACTION_MARK_READ,
+ )
+
+ action_parameter = models.CharField(
+ max_length=256, blank=True, null=True,
+ help_text="Additional parameter for the action selected above, i.e., "
+ "the target folder of the move to folder action."
+ )
+
+ assign_title_from = models.PositiveIntegerField(
+ choices=TITLE_SELECTOR,
+ default=TITLE_FROM_SUBJECT
+ )
+
+ assign_tag = models.ForeignKey(
+ document_models.Tag,
+ null=True,
+ blank=True,
+ on_delete=models.SET_NULL
+ )
+
+ assign_document_type = models.ForeignKey(
+ document_models.DocumentType,
+ null=True,
+ blank=True,
+ on_delete=models.SET_NULL
+ )
+
+ assign_correspondent_from = models.PositiveIntegerField(
+ choices=CORRESPONDENT_SELECTOR,
+ default=CORRESPONDENT_FROM_NOTHING
+ )
+
+ assign_correspondent = models.ForeignKey(
+ document_models.Correspondent,
+ null=True,
+ blank=True,
+ on_delete=models.SET_NULL
+ )
+
+ def __str__(self):
+ return f"{self.account.name}.{self.name}"
diff --git a/src/paperless_mail/tasks.py b/src/paperless_mail/tasks.py
new file mode 100644
index 000000000..68fb859a4
--- /dev/null
+++ b/src/paperless_mail/tasks.py
@@ -0,0 +1,30 @@
+import logging
+
+from paperless_mail.mail import MailAccountHandler, MailError
+from paperless_mail.models import MailAccount
+
+
+def process_mail_accounts():
+ total_new_documents = 0
+ for account in MailAccount.objects.all():
+ try:
+ total_new_documents += MailAccountHandler().handle_mail_account(
+ account)
+ except MailError as e:
+ logging.getLogger(__name__).error(
+ f"Error while processing mail account {account}: {e}",
+ exc_info=True
+ )
+
+ if total_new_documents > 0:
+ return f"Added {total_new_documents} document(s)."
+ else:
+ return "No new documents were added."
+
+
+def process_mail_account(name):
+ try:
+ account = MailAccount.objects.get(name=name)
+ MailAccountHandler().handle_mail_account(account)
+ except MailAccount.DoesNotExist:
+ logging.getLogger(__name__).error(f"Unknown mail acccount: {name}")
diff --git a/src/paperless_mail/tests/__init__.py b/src/paperless_mail/tests/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/paperless_mail/tests/test_mail.py b/src/paperless_mail/tests/test_mail.py
new file mode 100644
index 000000000..9c0f52c53
--- /dev/null
+++ b/src/paperless_mail/tests/test_mail.py
@@ -0,0 +1,498 @@
+import uuid
+from collections import namedtuple
+from typing import ContextManager
+from unittest import mock
+
+from django.core.management import call_command
+from django.db import DatabaseError
+from django.test import TestCase
+from imap_tools import MailMessageFlags, MailboxFolderSelectError
+
+from documents.models import Correspondent
+from paperless_mail import tasks
+from paperless_mail.mail import MailError, MailAccountHandler
+from paperless_mail.models import MailRule, MailAccount
+
+
+class BogusFolderManager:
+
+ current_folder = "INBOX"
+
+ def set(self, new_folder):
+ if new_folder not in ["INBOX", "spam"]:
+ raise MailboxFolderSelectError(None, "uhm")
+ self.current_folder = new_folder
+
+
+class BogusMailBox(ContextManager):
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ pass
+
+ def __init__(self):
+ self.messages = []
+ self.messages_spam = []
+
+ def login(self, username, password):
+ if not (username == 'admin' and password == 'secret'):
+ raise Exception()
+
+ folder = BogusFolderManager()
+
+ def fetch(self, criteria, mark_seen):
+ msg = self.messages
+
+ criteria = str(criteria).strip('()').split(" ")
+
+ if 'UNSEEN' in criteria:
+ msg = filter(lambda m: not m.seen, msg)
+
+ if 'SUBJECT' in criteria:
+ subject = criteria[criteria.index('SUBJECT') + 1].strip('"')
+ msg = filter(lambda m: subject in m.subject, msg)
+
+ if 'BODY' in criteria:
+ body = criteria[criteria.index('BODY') + 1].strip('"')
+ msg = filter(lambda m: body in m.body, msg)
+
+ if 'FROM' in criteria:
+ from_ = criteria[criteria.index('FROM') + 1].strip('"')
+ msg = filter(lambda m: from_ in m.from_, msg)
+
+ if 'UNFLAGGED' in criteria:
+ msg = filter(lambda m: not m.flagged, msg)
+
+ return list(msg)
+
+ def seen(self, uid_list, seen_val):
+ for message in self.messages:
+ if message.uid in uid_list:
+ message.seen = seen_val
+
+ def delete(self, uid_list):
+ self.messages = list(filter(lambda m: m.uid not in uid_list, self.messages))
+
+ def flag(self, uid_list, flag_set, value):
+ for message in self.messages:
+ if message.uid in uid_list:
+ for flag in flag_set:
+ if flag == MailMessageFlags.FLAGGED:
+ message.flagged = value
+
+ def move(self, uid_list, folder):
+ if folder == "spam":
+ self.messages_spam.append(
+ filter(lambda m: m.uid in uid_list, self.messages)
+ )
+ self.messages = list(
+ filter(lambda m: m.uid not in uid_list, self.messages)
+ )
+ else:
+ raise Exception()
+
+
+def create_message(num_attachments=1, body="", subject="the suject", from_="noone@mail.com", seen=False, flagged=False):
+ message = namedtuple('MailMessage', [])
+
+ message.uid = uuid.uuid4()
+ message.subject = subject
+ message.attachments = []
+ message.from_ = from_
+ message.body = body
+ for i in range(num_attachments):
+ message.attachments.append(create_attachment(filename=f"file_{i}.pdf"))
+
+ message.seen = seen
+ message.flagged = flagged
+
+ return message
+
+
+def create_attachment(filename="the_file.pdf", content_disposition="attachment", payload=b"a PDF document"):
+ attachment = namedtuple('Attachment', [])
+ attachment.filename = filename
+ attachment.content_disposition = content_disposition
+ attachment.payload = payload
+ return attachment
+
+
+def fake_magic_from_buffer(buffer, mime=False):
+
+ if mime:
+ if 'PDF' in str(buffer):
+ return 'application/pdf'
+ else:
+ return 'unknown/type'
+ else:
+ return 'Some verbose file description'
+
+
+@mock.patch('paperless_mail.mail.magic.from_buffer', fake_magic_from_buffer)
+class TestMail(TestCase):
+
+ def setUp(self):
+ patcher = mock.patch('paperless_mail.mail.MailBox')
+ m = patcher.start()
+ self.bogus_mailbox = BogusMailBox()
+ m.return_value = self.bogus_mailbox
+ self.addCleanup(patcher.stop)
+
+ patcher = mock.patch('paperless_mail.mail.async_task')
+ self.async_task = patcher.start()
+ self.addCleanup(patcher.stop)
+
+ self.reset_bogus_mailbox()
+
+ self.mail_account_handler = MailAccountHandler()
+
+ def reset_bogus_mailbox(self):
+ self.bogus_mailbox.messages = []
+ self.bogus_mailbox.messages_spam = []
+ self.bogus_mailbox.messages.append(create_message(subject="Invoice 1", from_="amazon@amazon.de", body="cables", seen=True, flagged=False))
+ self.bogus_mailbox.messages.append(create_message(subject="Invoice 2", body="from my favorite electronic store", seen=False, flagged=True))
+ self.bogus_mailbox.messages.append(create_message(subject="Claim your $10M price now!", from_="amazon@amazon-some-indian-site.org", seen=False))
+
+ def test_get_correspondent(self):
+ message = namedtuple('MailMessage', [])
+ message.from_ = "someone@somewhere.com"
+ message.from_values = {'name': "Someone!", 'email': "someone@somewhere.com"}
+
+ message2 = namedtuple('MailMessage', [])
+ message2.from_ = "me@localhost.com"
+ message2.from_values = {'name': "", 'email': "fake@localhost.com"}
+
+ me_localhost = Correspondent.objects.create(name=message2.from_)
+ someone_else = Correspondent.objects.create(name="someone else")
+
+ handler = MailAccountHandler()
+
+ rule = MailRule(name="a", assign_correspondent_from=MailRule.CORRESPONDENT_FROM_NOTHING)
+ self.assertIsNone(handler.get_correspondent(message, rule))
+
+ rule = MailRule(name="b", assign_correspondent_from=MailRule.CORRESPONDENT_FROM_EMAIL)
+ c = handler.get_correspondent(message, rule)
+ self.assertIsNotNone(c)
+ self.assertEqual(c.name, "someone@somewhere.com")
+ c = handler.get_correspondent(message2, rule)
+ self.assertIsNotNone(c)
+ self.assertEqual(c.name, "me@localhost.com")
+ self.assertEqual(c.id, me_localhost.id)
+
+ rule = MailRule(name="c", assign_correspondent_from=MailRule.CORRESPONDENT_FROM_NAME)
+ c = handler.get_correspondent(message, rule)
+ self.assertIsNotNone(c)
+ self.assertEqual(c.name, "Someone!")
+ c = handler.get_correspondent(message2, rule)
+ self.assertIsNotNone(c)
+ self.assertEqual(c.id, me_localhost.id)
+
+ rule = MailRule(name="d", assign_correspondent_from=MailRule.CORRESPONDENT_FROM_CUSTOM, assign_correspondent=someone_else)
+ c = handler.get_correspondent(message, rule)
+ self.assertEqual(c, someone_else)
+
+ def test_get_title(self):
+ message = namedtuple('MailMessage', [])
+ message.subject = "the message title"
+ att = namedtuple('Attachment', [])
+ att.filename = "this_is_the_file.pdf"
+
+ handler = MailAccountHandler()
+
+ rule = MailRule(name="a", assign_title_from=MailRule.TITLE_FROM_FILENAME)
+ self.assertEqual(handler.get_title(message, att, rule), "this_is_the_file")
+ rule = MailRule(name="b", assign_title_from=MailRule.TITLE_FROM_SUBJECT)
+ self.assertEqual(handler.get_title(message, att, rule), "the message title")
+
+ def test_handle_message(self):
+ message = create_message(subject="the message title", from_="Myself", num_attachments=2)
+
+ account = MailAccount()
+ rule = MailRule(assign_title_from=MailRule.TITLE_FROM_FILENAME, account=account)
+
+ result = self.mail_account_handler.handle_message(message, rule)
+
+ self.assertEqual(result, 2)
+
+ self.assertEqual(len(self.async_task.call_args_list), 2)
+
+ args1, kwargs1 = self.async_task.call_args_list[0]
+ args2, kwargs2 = self.async_task.call_args_list[1]
+
+ self.assertEqual(kwargs1['override_title'], "file_0")
+ self.assertEqual(kwargs1['override_filename'], "file_0.pdf")
+
+ self.assertEqual(kwargs2['override_title'], "file_1")
+ self.assertEqual(kwargs2['override_filename'], "file_1.pdf")
+
+ def test_handle_empty_message(self):
+ message = namedtuple('MailMessage', [])
+
+ message.attachments = []
+ rule = MailRule()
+
+ result = self.mail_account_handler.handle_message(message, rule)
+
+ self.assertFalse(self.async_task.called)
+ self.assertEqual(result, 0)
+
+ def test_handle_unknown_mime_type(self):
+ message = create_message()
+ message.attachments = [
+ create_attachment(filename="f1.pdf"),
+ create_attachment(filename="f2.json", payload=b"{'much': 'payload.', 'so': 'json', 'wow': true}")
+ ]
+
+ account = MailAccount()
+ rule = MailRule(assign_title_from=MailRule.TITLE_FROM_FILENAME, account=account)
+
+ result = self.mail_account_handler.handle_message(message, rule)
+
+ self.assertEqual(result, 1)
+ self.assertEqual(self.async_task.call_count, 1)
+
+ args, kwargs = self.async_task.call_args
+ self.assertEqual(kwargs['override_filename'], "f1.pdf")
+
+ def test_handle_disposition(self):
+ message = create_message()
+ message.attachments = [
+ create_attachment(filename="f1.pdf", content_disposition='inline'),
+ create_attachment(filename="f2.pdf", content_disposition='attachment')
+ ]
+
+ account = MailAccount()
+ rule = MailRule(assign_title_from=MailRule.TITLE_FROM_FILENAME, account=account)
+
+ result = self.mail_account_handler.handle_message(message, rule)
+
+ self.assertEqual(result, 1)
+ self.assertEqual(self.async_task.call_count, 1)
+
+ args, kwargs = self.async_task.call_args
+ self.assertEqual(kwargs['override_filename'], "f2.pdf")
+
+ def test_handle_mail_account_mark_read(self):
+
+ account = MailAccount.objects.create(name="test", imap_server="", username="admin", password="secret")
+
+ rule = MailRule.objects.create(name="testrule", account=account, action=MailRule.ACTION_MARK_READ)
+
+ self.assertEqual(len(self.bogus_mailbox.messages), 3)
+ self.assertEqual(self.async_task.call_count, 0)
+ self.assertEqual(len(self.bogus_mailbox.fetch("UNSEEN", False)), 2)
+ self.mail_account_handler.handle_mail_account(account)
+ self.assertEqual(self.async_task.call_count, 2)
+ self.assertEqual(len(self.bogus_mailbox.fetch("UNSEEN", False)), 0)
+ self.assertEqual(len(self.bogus_mailbox.messages), 3)
+
+ def test_handle_mail_account_delete(self):
+
+ account = MailAccount.objects.create(name="test", imap_server="", username="admin", password="secret")
+
+ rule = MailRule.objects.create(name="testrule", account=account, action=MailRule.ACTION_DELETE, filter_subject="Invoice")
+
+ self.assertEqual(self.async_task.call_count, 0)
+ self.assertEqual(len(self.bogus_mailbox.messages), 3)
+ self.mail_account_handler.handle_mail_account(account)
+ self.assertEqual(self.async_task.call_count, 2)
+ self.assertEqual(len(self.bogus_mailbox.messages), 1)
+
+ def test_handle_mail_account_flag(self):
+ account = MailAccount.objects.create(name="test", imap_server="", username="admin", password="secret")
+
+ rule = MailRule.objects.create(name="testrule", account=account, action=MailRule.ACTION_FLAG, filter_subject="Invoice")
+
+ self.assertEqual(len(self.bogus_mailbox.messages), 3)
+ self.assertEqual(self.async_task.call_count, 0)
+ self.assertEqual(len(self.bogus_mailbox.fetch("UNFLAGGED", False)), 2)
+ self.mail_account_handler.handle_mail_account(account)
+ self.assertEqual(self.async_task.call_count, 1)
+ self.assertEqual(len(self.bogus_mailbox.fetch("UNFLAGGED", False)), 1)
+ self.assertEqual(len(self.bogus_mailbox.messages), 3)
+
+ def test_handle_mail_account_move(self):
+ account = MailAccount.objects.create(name="test", imap_server="", username="admin", password="secret")
+
+ rule = MailRule.objects.create(name="testrule", account=account, action=MailRule.ACTION_MOVE, action_parameter="spam", filter_subject="Claim")
+
+ self.assertEqual(self.async_task.call_count, 0)
+ self.assertEqual(len(self.bogus_mailbox.messages), 3)
+ self.assertEqual(len(self.bogus_mailbox.messages_spam), 0)
+ self.mail_account_handler.handle_mail_account(account)
+ self.assertEqual(self.async_task.call_count, 1)
+ self.assertEqual(len(self.bogus_mailbox.messages), 2)
+ self.assertEqual(len(self.bogus_mailbox.messages_spam), 1)
+
+ def test_error_login(self):
+ account = MailAccount.objects.create(name="test", imap_server="", username="admin", password="wrong")
+
+ try:
+ self.mail_account_handler.handle_mail_account(account)
+ except MailError as e:
+ self.assertTrue(str(e).startswith("Error while authenticating account"))
+ else:
+ self.fail("Should raise exception")
+
+ def test_error_skip_account(self):
+ account_faulty = MailAccount.objects.create(name="test", imap_server="", username="admin", password="wroasdng")
+
+ account = MailAccount.objects.create(name="test2", imap_server="", username="admin", password="secret")
+ rule = MailRule.objects.create(name="testrule", account=account, action=MailRule.ACTION_MOVE,
+ action_parameter="spam", filter_subject="Claim")
+
+ tasks.process_mail_accounts()
+ self.assertEqual(self.async_task.call_count, 1)
+ self.assertEqual(len(self.bogus_mailbox.messages), 2)
+ self.assertEqual(len(self.bogus_mailbox.messages_spam), 1)
+
+ def test_error_skip_rule(self):
+
+ account = MailAccount.objects.create(name="test2", imap_server="", username="admin", password="secret")
+ rule = MailRule.objects.create(name="testrule", account=account, action=MailRule.ACTION_MOVE,
+ action_parameter="spam", filter_subject="Claim", order=1, folder="uuuhhhh")
+ rule2 = MailRule.objects.create(name="testrule2", account=account, action=MailRule.ACTION_MOVE,
+ action_parameter="spam", filter_subject="Claim", order=2)
+
+ self.mail_account_handler.handle_mail_account(account)
+ self.assertEqual(self.async_task.call_count, 1)
+ self.assertEqual(len(self.bogus_mailbox.messages), 2)
+ self.assertEqual(len(self.bogus_mailbox.messages_spam), 1)
+
+
+ @mock.patch("paperless_mail.mail.MailAccountHandler.get_correspondent")
+ def test_error_skip_mail(self, m):
+
+ def get_correspondent_fake(message, rule):
+ if message.from_ == 'amazon@amazon.de':
+ raise ValueError("Does not compute.")
+ else:
+ return None
+
+ m.side_effect = get_correspondent_fake
+
+ account = MailAccount.objects.create(name="test2", imap_server="", username="admin", password="secret")
+ rule = MailRule.objects.create(name="testrule", account=account, action=MailRule.ACTION_MOVE, action_parameter="spam")
+
+ self.mail_account_handler.handle_mail_account(account)
+
+ # test that we still consume mail even if some mails throw errors.
+ self.assertEqual(self.async_task.call_count, 2)
+
+ # faulty mail still in inbox, untouched
+ self.assertEqual(len(self.bogus_mailbox.messages), 1)
+ self.assertEqual(self.bogus_mailbox.messages[0].from_, 'amazon@amazon.de')
+
+ def test_error_create_correspondent(self):
+
+ account = MailAccount.objects.create(name="test2", imap_server="", username="admin", password="secret")
+ rule = MailRule.objects.create(
+ name="testrule", filter_from="amazon@amazon.de",
+ account=account, action=MailRule.ACTION_MOVE, action_parameter="spam",
+ assign_correspondent_from=MailRule.CORRESPONDENT_FROM_EMAIL)
+
+ self.mail_account_handler.handle_mail_account(account)
+
+ self.async_task.assert_called_once()
+ args, kwargs = self.async_task.call_args
+
+ c = Correspondent.objects.get(name="amazon@amazon.de")
+ # should work
+ self.assertEqual(kwargs['override_correspondent_id'], c.id)
+
+ self.async_task.reset_mock()
+ self.reset_bogus_mailbox()
+
+ with mock.patch("paperless_mail.mail.Correspondent.objects.get_or_create") as m:
+ m.side_effect = DatabaseError()
+
+ self.mail_account_handler.handle_mail_account(account)
+
+ args, kwargs = self.async_task.call_args
+ self.async_task.assert_called_once()
+ self.assertEqual(kwargs['override_correspondent_id'], None)
+
+
+ def test_filters(self):
+
+ account = MailAccount.objects.create(name="test3", imap_server="", username="admin", password="secret")
+ rule = MailRule.objects.create(name="testrule3", account=account, action=MailRule.ACTION_DELETE, filter_subject="Claim")
+
+ self.assertEqual(self.async_task.call_count, 0)
+
+ self.assertEqual(len(self.bogus_mailbox.messages), 3)
+ self.mail_account_handler.handle_mail_account(account)
+ self.assertEqual(len(self.bogus_mailbox.messages), 2)
+ self.assertEqual(self.async_task.call_count, 1)
+
+ self.reset_bogus_mailbox()
+
+ rule.filter_subject = None
+ rule.filter_body = "electronic"
+ rule.save()
+ self.assertEqual(len(self.bogus_mailbox.messages), 3)
+ self.mail_account_handler.handle_mail_account(account)
+ self.assertEqual(len(self.bogus_mailbox.messages), 2)
+ self.assertEqual(self.async_task.call_count, 2)
+
+ self.reset_bogus_mailbox()
+
+ rule.filter_from = "amazon"
+ rule.filter_body = None
+ rule.save()
+ self.assertEqual(len(self.bogus_mailbox.messages), 3)
+ self.mail_account_handler.handle_mail_account(account)
+ self.assertEqual(len(self.bogus_mailbox.messages), 1)
+ self.assertEqual(self.async_task.call_count, 4)
+
+ self.reset_bogus_mailbox()
+
+ rule.filter_from = "amazon"
+ rule.filter_body = "cables"
+ rule.filter_subject = "Invoice"
+ rule.save()
+ self.assertEqual(len(self.bogus_mailbox.messages), 3)
+ self.mail_account_handler.handle_mail_account(account)
+ self.assertEqual(len(self.bogus_mailbox.messages), 2)
+ self.assertEqual(self.async_task.call_count, 5)
+
+class TestManagementCommand(TestCase):
+
+ @mock.patch("paperless_mail.management.commands.mail_fetcher.tasks.process_mail_accounts")
+ def test_mail_fetcher(self, m):
+
+ call_command("mail_fetcher")
+
+ m.assert_called_once()
+
+class TestTasks(TestCase):
+
+ @mock.patch("paperless_mail.tasks.MailAccountHandler.handle_mail_account")
+ def test_all_accounts(self, m):
+ m.side_effect = lambda account: 6
+
+ MailAccount.objects.create(name="A", imap_server="A", username="A", password="A")
+ MailAccount.objects.create(name="B", imap_server="A", username="A", password="A")
+
+ result = tasks.process_mail_accounts()
+
+ self.assertEqual(m.call_count, 2)
+ self.assertIn("Added 12", result)
+
+ m.side_effect = lambda account: 0
+ result = tasks.process_mail_accounts()
+ self.assertIn("No new", result)
+
+ @mock.patch("paperless_mail.tasks.MailAccountHandler.handle_mail_account")
+ def test_single_accounts(self, m):
+
+ MailAccount.objects.create(name="A", imap_server="A", username="A", password="A")
+
+ tasks.process_mail_account("A")
+
+ m.assert_called_once()
+ m.reset_mock()
+
+ tasks.process_mail_account("B")
+ m.assert_not_called()
diff --git a/src/paperless_tesseract/__init__.py b/src/paperless_tesseract/__init__.py
index e69de29bb..5c9f358c3 100644
--- a/src/paperless_tesseract/__init__.py
+++ b/src/paperless_tesseract/__init__.py
@@ -0,0 +1,2 @@
+# this is here so that django finds the checks.
+from .checks import *
diff --git a/src/paperless_tesseract/apps.py b/src/paperless_tesseract/apps.py
index bdb430bea..67b90f006 100644
--- a/src/paperless_tesseract/apps.py
+++ b/src/paperless_tesseract/apps.py
@@ -1,5 +1,7 @@
from django.apps import AppConfig
+from paperless_tesseract.signals import tesseract_consumer_declaration
+
class PaperlessTesseractConfig(AppConfig):
@@ -9,8 +11,6 @@ class PaperlessTesseractConfig(AppConfig):
from documents.signals import document_consumer_declaration
- from .signals import ConsumerDeclaration
-
- document_consumer_declaration.connect(ConsumerDeclaration.handle)
+ document_consumer_declaration.connect(tesseract_consumer_declaration)
AppConfig.ready(self)
diff --git a/src/paperless_tesseract/checks.py b/src/paperless_tesseract/checks.py
new file mode 100644
index 000000000..41ea3c9b5
--- /dev/null
+++ b/src/paperless_tesseract/checks.py
@@ -0,0 +1,34 @@
+import subprocess
+
+from django.conf import settings
+from django.core.checks import Error, register
+
+
+def get_tesseract_langs():
+ with subprocess.Popen(['tesseract', '--list-langs'],
+ stdout=subprocess.PIPE) as p:
+ stdout, stderr = p.communicate()
+
+ return stdout.decode().strip().split("\n")[1:]
+
+
+@register()
+def check_default_language_available(app_configs, **kwargs):
+ installed_langs = get_tesseract_langs()
+
+ if not settings.OCR_LANGUAGE:
+ return [Warning(
+ "No OCR language has been specified with PAPERLESS_OCR_LANGUAGE. "
+ "This means that tesseract will fallback to english."
+ )]
+
+ specified_langs = settings.OCR_LANGUAGE.split("+")
+
+ for lang in specified_langs:
+ if lang not in installed_langs:
+ return [Error(
+ f"The selected ocr language {lang} is "
+ f"not installed. Paperless cannot OCR your documents "
+ f"without it. Please fix PAPERLESS_OCR_LANGUAGE.")]
+
+ return []
diff --git a/src/paperless_tesseract/parsers.py b/src/paperless_tesseract/parsers.py
index fd7077834..80e200f27 100644
--- a/src/paperless_tesseract/parsers.py
+++ b/src/paperless_tesseract/parsers.py
@@ -1,25 +1,16 @@
-import itertools
+import json
import os
import re
import subprocess
-from multiprocessing.pool import Pool
-
-import langdetect
-import pyocr
-from django.conf import settings
-from PIL import Image
-from pyocr.libtesseract.tesseract_raw import \
- TesseractError as OtherTesseractError
-from pyocr.tesseract import TesseractError
+import ocrmypdf
import pdftotext
-from documents.parsers import DocumentParser, ParseError
+import pikepdf
+from PIL import Image
+from django.conf import settings
+from ocrmypdf import InputFileError, EncryptedPdfError
-from .languages import ISO639
-
-
-class OCRError(Exception):
- pass
+from documents.parsers import DocumentParser, ParseError, run_convert
class RasterisedDocumentParser(DocumentParser):
@@ -28,19 +19,34 @@ class RasterisedDocumentParser(DocumentParser):
image, whether it's a PDF, or other graphical format (JPEG, TIFF, etc.)
"""
- CONVERT = settings.CONVERT_BINARY
- GHOSTSCRIPT = settings.GS_BINARY
- DENSITY = settings.CONVERT_DENSITY if settings.CONVERT_DENSITY else 300
- THREADS = int(settings.OCR_THREADS) if settings.OCR_THREADS else None
- UNPAPER = settings.UNPAPER_BINARY
- DEFAULT_OCR_LANGUAGE = settings.OCR_LANGUAGE
- OCR_ALWAYS = settings.OCR_ALWAYS
+ def extract_metadata(self, document_path, mime_type):
+ namespace_pattern = re.compile(r"\{(.*)\}(.*)")
- def __init__(self, path):
- super().__init__(path)
- self._text = None
+ result = []
+ if mime_type == 'application/pdf':
+ pdf = pikepdf.open(document_path)
+ meta = pdf.open_metadata()
+ for key, value in meta.items():
+ if isinstance(value, list):
+ value = " ".join([str(e) for e in value])
+ value = str(value)
+ try:
+ m = namespace_pattern.match(key)
+ result.append({
+ "namespace": m.group(1),
+ "prefix": meta.REVERSE_NS[m.group(1)],
+ "key": m.group(2),
+ "value": value
+ })
+ except Exception as e:
+ self.log(
+ "warning",
+ f"Error while reading metadata {key}: {value}. Error: "
+ f"{e}"
+ )
+ return result
- def get_thumbnail(self):
+ def get_thumbnail(self, document_path, mime_type):
"""
The thumbnail of a PDF is just a 500px wide image of the first page.
"""
@@ -49,240 +55,223 @@ class RasterisedDocumentParser(DocumentParser):
# Run convert to get a decent thumbnail
try:
- run_convert(
- self.CONVERT,
- "-scale", "500x5000",
- "-alpha", "remove",
- "-strip", "-trim",
- "{}[0]".format(self.document_path),
- out_path
- )
+ run_convert(density=300,
+ scale="500x5000>",
+ alpha="remove",
+ strip=True,
+ trim=False,
+ input_file="{}[0]".format(document_path),
+ output_file=out_path,
+ logging_group=self.logging_group)
except ParseError:
# if convert fails, fall back to extracting
# the first PDF page as a PNG using Ghostscript
self.log(
- "warning",
- "Thumbnail generation with ImageMagick failed, "
- "falling back to Ghostscript."
- )
+ 'warning',
+ "Thumbnail generation with ImageMagick failed, falling back "
+ "to ghostscript. Check your /etc/ImageMagick-x/policy.xml!")
gs_out_path = os.path.join(self.tempdir, "gs_out.png")
- cmd = [self.GHOSTSCRIPT,
+ cmd = [settings.GS_BINARY,
"-q",
"-sDEVICE=pngalpha",
"-o", gs_out_path,
- self.document_path]
+ document_path]
if not subprocess.Popen(cmd).wait() == 0:
raise ParseError("Thumbnail (gs) failed at {}".format(cmd))
# then run convert on the output from gs
- run_convert(
- self.CONVERT,
- "-scale", "500x5000",
- "-alpha", "remove",
- "-strip", "-trim",
- gs_out_path,
- out_path
- )
+ run_convert(density=300,
+ scale="500x5000>",
+ alpha="remove",
+ strip=True,
+ trim=False,
+ input_file=gs_out_path,
+ output_file=out_path,
+ logging_group=self.logging_group)
return out_path
- def _is_ocred(self):
+ def is_image(self, mime_type):
+ return mime_type in [
+ "image/png",
+ "image/jpeg",
+ "image/tiff",
+ "image/bmp",
+ "image/gif",
+ ]
- # Extract text from PDF using pdftotext
- text = get_text_from_pdf(self.document_path)
+ def get_dpi(self, image):
+ try:
+ with Image.open(image) as im:
+ x, y = im.info['dpi']
+ return x
+ except Exception as e:
+ self.log(
+ 'warning',
+ f"Error while getting DPI from image {image}: {e}")
+ return None
- # We assume, that a PDF with at least 50 characters contains text
- # (so no OCR required)
- return len(text) > 50
+ def calculate_a4_dpi(self, image):
+ try:
+ with Image.open(image) as im:
+ width, height = im.size
+ # divide image width by A4 width (210mm) in inches.
+ dpi = int(width / (21 / 2.54))
+ self.log(
+ 'debug',
+ f"Estimated DPI {dpi} based on image width {width}"
+ )
+ return dpi
- def get_text(self):
+ except Exception as e:
+ self.log(
+ 'warning',
+ f"Error while calculating DPI for image {image}: {e}")
+ return None
- if self._text is not None:
- return self._text
+ def parse(self, document_path, mime_type):
+ mode = settings.OCR_MODE
- if not self.OCR_ALWAYS and self._is_ocred():
- self.log("info", "Skipping OCR, using Text from PDF")
- self._text = get_text_from_pdf(self.document_path)
- return self._text
+ text_original = get_text_from_pdf(document_path)
+ has_text = text_original and len(text_original) > 50
- images = self._get_greyscale()
+ if mode == "skip_noarchive" and has_text:
+ self.log("debug",
+ "Document has text, skipping OCRmyPDF entirely.")
+ self.text = text_original
+ return
+
+ if mode in ['skip', 'skip_noarchive'] and not has_text:
+ # upgrade to redo, since there appears to be no text in the
+ # document. This happens to some weird encrypted documents or
+ # documents with failed OCR attempts for which OCRmyPDF will
+ # still report that there actually is text in them.
+ self.log("debug",
+ "No text was found in the document and skip is "
+ "specified. Upgrading OCR mode to redo.")
+ mode = "redo"
+
+ archive_path = os.path.join(self.tempdir, "archive.pdf")
+
+ ocr_args = {
+ 'input_file': document_path,
+ 'output_file': archive_path,
+ 'use_threads': True,
+ 'jobs': settings.THREADS_PER_WORKER,
+ 'language': settings.OCR_LANGUAGE,
+ 'output_type': settings.OCR_OUTPUT_TYPE,
+ 'progress_bar': False,
+ 'clean': True
+ }
+
+ if settings.OCR_PAGES > 0:
+ ocr_args['pages'] = f"1-{settings.OCR_PAGES}"
+
+ # Mode selection.
+
+ if mode in ['skip', 'skip_noarchive']:
+ ocr_args['skip_text'] = True
+ elif mode == 'redo':
+ ocr_args['redo_ocr'] = True
+ elif mode == 'force':
+ ocr_args['force_ocr'] = True
+ else:
+ raise ParseError(
+ f"Invalid ocr mode: {mode}")
+
+ if self.is_image(mime_type):
+ dpi = self.get_dpi(document_path)
+ a4_dpi = self.calculate_a4_dpi(document_path)
+ if dpi:
+ self.log(
+ "debug",
+ f"Detected DPI for image {document_path}: {dpi}"
+ )
+ ocr_args['image_dpi'] = dpi
+ elif settings.OCR_IMAGE_DPI:
+ ocr_args['image_dpi'] = settings.OCR_IMAGE_DPI
+ elif a4_dpi:
+ ocr_args['image_dpi'] = a4_dpi
+ else:
+ raise ParseError(
+ f"Cannot produce archive PDF for image {document_path}, "
+ f"no DPI information is present in this image and "
+ f"OCR_IMAGE_DPI is not set.")
+
+ if settings.OCR_USER_ARGS:
+ try:
+ user_args = json.loads(settings.OCR_USER_ARGS)
+ ocr_args = {**ocr_args, **user_args}
+ except Exception as e:
+ self.log(
+ "warning",
+ f"There is an issue with PAPERLESS_OCR_USER_ARGS, so "
+ f"they will not be used: {e}")
+
+ # This forces tesseract to use one core per page.
+ os.environ['OMP_THREAD_LIMIT'] = "1"
try:
- self._text = self._get_ocr(images)
- return self._text
- except OCRError as e:
+ self.log("debug",
+ f"Calling OCRmyPDF with {str(ocr_args)}")
+ ocrmypdf.ocr(**ocr_args)
+ # success! announce results
+ self.archive_path = archive_path
+ self.text = get_text_from_pdf(archive_path)
+
+ except (InputFileError, EncryptedPdfError) as e:
+
+ self.log("debug",
+ f"Encountered an error: {e}. Trying to use text from "
+ f"original.")
+ # This happens with some PDFs when used with the redo_ocr option.
+ # This is not the end of the world, we'll just use what we already
+ # have in the document.
+ self.text = text_original
+ # Also, no archived file.
+ if not self.text:
+ # However, if we don't have anything, fail:
+ raise ParseError(e)
+
+ except Exception as e:
+ # Anything else is probably serious.
raise ParseError(e)
- def _get_greyscale(self):
- """
- Greyscale images are easier for Tesseract to OCR
- """
-
- # Convert PDF to multiple PNMs
- pnm = os.path.join(self.tempdir, "convert-%04d.pnm")
- run_convert(
- self.CONVERT,
- "-density", str(self.DENSITY),
- "-depth", "8",
- "-type", "grayscale",
- self.document_path, pnm,
- )
-
- # Get a list of converted images
- pnms = []
- for f in os.listdir(self.tempdir):
- if f.endswith(".pnm"):
- pnms.append(os.path.join(self.tempdir, f))
-
- # Run unpaper in parallel on converted images
- with Pool(processes=self.THREADS) as pool:
- pool.map(run_unpaper, itertools.product([self.UNPAPER], pnms))
-
- # Return list of converted images, processed with unpaper
- pnms = []
- for f in os.listdir(self.tempdir):
- if f.endswith(".unpaper.pnm"):
- pnms.append(os.path.join(self.tempdir, f))
-
- return sorted(filter(lambda __: os.path.isfile(__), pnms))
-
- def _guess_language(self, text):
- try:
- guess = langdetect.detect(text)
- self.log("debug", "Language detected: {}".format(guess))
- return guess
- except Exception as e:
- self.log("warning", "Language detection error: {}".format(e))
-
- def _get_ocr(self, imgs):
- """
- Attempts to do the best job possible OCR'ing the document based on
- simple language detection trial & error.
- """
-
- if not imgs:
- raise OCRError("No images found")
-
- self.log("info", "OCRing the document")
-
- # Since the division gets rounded down by int, this calculation works
- # for every edge-case, i.e. 1
- middle = int(len(imgs) / 2)
- raw_text = self._ocr([imgs[middle]], self.DEFAULT_OCR_LANGUAGE)
-
- guessed_language = self._guess_language(raw_text)
-
- if not guessed_language or guessed_language not in ISO639:
- self.log("warning", "Language detection failed!")
- if settings.FORGIVING_OCR:
- self.log(
- "warning",
- "As FORGIVING_OCR is enabled, we're going to make the "
- "best with what we have."
- )
- raw_text = self._assemble_ocr_sections(imgs, middle, raw_text)
- return raw_text
- error_msg = ("Language detection failed. Set "
- "PAPERLESS_FORGIVING_OCR in config file to continue "
- "anyway.")
- raise OCRError(error_msg)
-
- if ISO639[guessed_language] == self.DEFAULT_OCR_LANGUAGE:
- raw_text = self._assemble_ocr_sections(imgs, middle, raw_text)
- return raw_text
-
- try:
- return self._ocr(imgs, ISO639[guessed_language])
- except pyocr.pyocr.tesseract.TesseractError:
- if settings.FORGIVING_OCR:
- self.log(
- "warning",
- "OCR for {} failed, but we're going to stick with what "
- "we've got since FORGIVING_OCR is enabled.".format(
- guessed_language
- )
- )
- raw_text = self._assemble_ocr_sections(imgs, middle, raw_text)
- return raw_text
- raise OCRError(
- "The guessed language ({}) is not available in this instance "
- "of Tesseract.".format(guessed_language)
- )
-
- def _ocr(self, imgs, lang):
- """
- Performs a single OCR attempt.
- """
-
- if not imgs:
- return ""
-
- self.log("info", "Parsing for {}".format(lang))
-
- with Pool(processes=self.THREADS) as pool:
- r = pool.map(image_to_string, itertools.product(imgs, [lang]))
- r = " ".join(r)
-
- # Strip out excess white space to allow matching to go smoother
- return strip_excess_whitespace(r)
-
- def _assemble_ocr_sections(self, imgs, middle, text):
- """
- Given a `middle` value and the text that middle page represents, we OCR
- the remainder of the document and return the whole thing.
- """
- text = self._ocr(imgs[:middle], self.DEFAULT_OCR_LANGUAGE) + text
- text += self._ocr(imgs[middle + 1:], self.DEFAULT_OCR_LANGUAGE)
- return text
-
-
-def run_convert(*args):
-
- environment = os.environ.copy()
- if settings.CONVERT_MEMORY_LIMIT:
- environment["MAGICK_MEMORY_LIMIT"] = settings.CONVERT_MEMORY_LIMIT
- if settings.CONVERT_TMPDIR:
- environment["MAGICK_TMPDIR"] = settings.CONVERT_TMPDIR
-
- if not subprocess.Popen(args, env=environment).wait() == 0:
- raise ParseError("Convert failed at {}".format(args))
-
-
-def run_unpaper(args):
- unpaper, pnm = args
- command_args = (unpaper, "--overwrite", pnm,
- pnm.replace(".pnm", ".unpaper.pnm"))
- if not subprocess.Popen(command_args).wait() == 0:
- raise ParseError("Unpaper failed at {}".format(command_args))
+ if not self.text:
+ # This may happen for files that don't have any text.
+ self.log(
+ 'warning',
+ f"Document {document_path} does not have any text."
+ f"This is probably an error or you tried to add an image "
+ f"without text, or something is wrong with this document.")
+ self.text = ""
def strip_excess_whitespace(text):
+ if not text:
+ return None
+
collapsed_spaces = re.sub(r"([^\S\r\n]+)", " ", text)
no_leading_whitespace = re.sub(
r"([\n\r]+)([^\S\n\r]+)", '\\1', collapsed_spaces)
no_trailing_whitespace = re.sub(
r"([^\S\n\r]+)$", '', no_leading_whitespace)
- return no_trailing_whitespace
-
-def image_to_string(args):
- img, lang = args
- ocr = pyocr.get_available_tools()[0]
- with Image.open(os.path.join(RasterisedDocumentParser.SCRATCH, img)) as f:
- if ocr.can_detect_orientation():
- try:
- orientation = ocr.detect_orientation(f, lang=lang)
- f = f.rotate(orientation["angle"], expand=1)
- except (TesseractError, OtherTesseractError, AttributeError):
- pass
- return ocr.image_to_string(f, lang=lang)
+ # TODO: this needs a rework
+ return no_trailing_whitespace.strip()
def get_text_from_pdf(pdf_file):
+ if not os.path.isfile(pdf_file):
+ return None
+
with open(pdf_file, "rb") as f:
try:
pdf = pdftotext.PDF(f)
except pdftotext.Error:
- return ""
+ # might not be a PDF file
+ return None
- return "\n".join(pdf)
+ text = "\n".join(pdf)
+
+ return strip_excess_whitespace(text)
diff --git a/src/paperless_tesseract/signals.py b/src/paperless_tesseract/signals.py
index 237f15c52..1e1cd1e1a 100644
--- a/src/paperless_tesseract/signals.py
+++ b/src/paperless_tesseract/signals.py
@@ -1,23 +1,16 @@
-import re
-
from .parsers import RasterisedDocumentParser
-class ConsumerDeclaration:
-
- MATCHING_FILES = re.compile(r"^.*\.(pdf|jpe?g|gif|png|tiff?|pnm|bmp)$")
-
- @classmethod
- def handle(cls, sender, **kwargs):
- return cls.test
-
- @classmethod
- def test(cls, doc):
-
- if cls.MATCHING_FILES.match(doc.lower()):
- return {
- "parser": RasterisedDocumentParser,
- "weight": 0
- }
-
- return None
+def tesseract_consumer_declaration(sender, **kwargs):
+ return {
+ "parser": RasterisedDocumentParser,
+ "weight": 0,
+ "mime_types": {
+ "application/pdf": ".pdf",
+ "image/jpeg": ".jpg",
+ "image/png": ".png",
+ "image/tiff": ".tif",
+ "image/gif": ".gif",
+ "image/bmp": ".bmp",
+ }
+ }
diff --git a/src/paperless_tesseract/tests/samples/multi-page-digital.pdf b/src/paperless_tesseract/tests/samples/multi-page-digital.pdf
new file mode 100644
index 000000000..5e75266ca
Binary files /dev/null and b/src/paperless_tesseract/tests/samples/multi-page-digital.pdf differ
diff --git a/src/paperless_tesseract/tests/samples/multi-page-images.pdf b/src/paperless_tesseract/tests/samples/multi-page-images.pdf
new file mode 100644
index 000000000..ea08363bf
Binary files /dev/null and b/src/paperless_tesseract/tests/samples/multi-page-images.pdf differ
diff --git a/src/paperless_tesseract/tests/samples/no-text-alpha.png b/src/paperless_tesseract/tests/samples/no-text-alpha.png
new file mode 100644
index 000000000..e78b22bfb
Binary files /dev/null and b/src/paperless_tesseract/tests/samples/no-text-alpha.png differ
diff --git a/src/paperless_tesseract/tests/samples/simple-alpha.png b/src/paperless_tesseract/tests/samples/simple-alpha.png
new file mode 100644
index 000000000..0a267db1f
Binary files /dev/null and b/src/paperless_tesseract/tests/samples/simple-alpha.png differ
diff --git a/src/paperless_tesseract/tests/samples/simple-digital.pdf b/src/paperless_tesseract/tests/samples/simple-digital.pdf
new file mode 100644
index 000000000..e450de482
Binary files /dev/null and b/src/paperless_tesseract/tests/samples/simple-digital.pdf differ
diff --git a/src/paperless_tesseract/tests/samples/simple-no-dpi.png b/src/paperless_tesseract/tests/samples/simple-no-dpi.png
new file mode 100644
index 000000000..84b2dc29b
Binary files /dev/null and b/src/paperless_tesseract/tests/samples/simple-no-dpi.png differ
diff --git a/src/paperless_tesseract/tests/samples/simple.bmp b/src/paperless_tesseract/tests/samples/simple.bmp
new file mode 100644
index 000000000..a25dee50f
Binary files /dev/null and b/src/paperless_tesseract/tests/samples/simple.bmp differ
diff --git a/src/paperless_tesseract/tests/samples/simple.gif b/src/paperless_tesseract/tests/samples/simple.gif
new file mode 100644
index 000000000..1c75f7442
Binary files /dev/null and b/src/paperless_tesseract/tests/samples/simple.gif differ
diff --git a/src/paperless_tesseract/tests/samples/simple.jpg b/src/paperless_tesseract/tests/samples/simple.jpg
new file mode 100644
index 000000000..53e55129a
Binary files /dev/null and b/src/paperless_tesseract/tests/samples/simple.jpg differ
diff --git a/src/paperless_tesseract/tests/samples/simple.png b/src/paperless_tesseract/tests/samples/simple.png
new file mode 100644
index 000000000..6fa0490e4
Binary files /dev/null and b/src/paperless_tesseract/tests/samples/simple.png differ
diff --git a/src/paperless_tesseract/tests/samples/simple.tif b/src/paperless_tesseract/tests/samples/simple.tif
new file mode 100644
index 000000000..1621f4496
Binary files /dev/null and b/src/paperless_tesseract/tests/samples/simple.tif differ
diff --git a/src/paperless_tesseract/tests/samples/with-form.pdf b/src/paperless_tesseract/tests/samples/with-form.pdf
new file mode 100644
index 000000000..afbeef5c8
Binary files /dev/null and b/src/paperless_tesseract/tests/samples/with-form.pdf differ
diff --git a/src/paperless_tesseract/tests/test_date.py b/src/paperless_tesseract/tests/test_date.py
deleted file mode 100644
index 9e9d48b90..000000000
--- a/src/paperless_tesseract/tests/test_date.py
+++ /dev/null
@@ -1,200 +0,0 @@
-import datetime
-import os
-import shutil
-from unittest import mock
-from uuid import uuid4
-
-from dateutil import tz
-from django.test import TestCase
-
-from ..parsers import RasterisedDocumentParser
-from django.conf import settings
-
-
-class TestDate(TestCase):
-
- SAMPLE_FILES = os.path.join(os.path.dirname(__file__), "samples")
- SCRATCH = "/tmp/paperless-tests-{}".format(str(uuid4())[:8])
-
- MOCK_SCRATCH = "paperless_tesseract.parsers.RasterisedDocumentParser.SCRATCH" # NOQA: E501
-
- def setUp(self):
- os.makedirs(self.SCRATCH, exist_ok=True)
-
- def tearDown(self):
- shutil.rmtree(self.SCRATCH)
-
- @mock.patch(MOCK_SCRATCH, SCRATCH)
- def test_date_format_1(self):
- input_file = os.path.join(self.SAMPLE_FILES, "")
- document = RasterisedDocumentParser(input_file)
- document._text = "lorem ipsum 130218 lorem ipsum"
- self.assertEqual(document.get_date(), None)
-
- @mock.patch(MOCK_SCRATCH, SCRATCH)
- def test_date_format_2(self):
- input_file = os.path.join(self.SAMPLE_FILES, "")
- document = RasterisedDocumentParser(input_file)
- document._text = "lorem ipsum 2018 lorem ipsum"
- self.assertEqual(document.get_date(), None)
-
- @mock.patch(MOCK_SCRATCH, SCRATCH)
- def test_date_format_3(self):
- input_file = os.path.join(self.SAMPLE_FILES, "")
- document = RasterisedDocumentParser(input_file)
- document._text = "lorem ipsum 20180213 lorem ipsum"
- self.assertEqual(document.get_date(), None)
-
- @mock.patch(MOCK_SCRATCH, SCRATCH)
- def test_date_format_4(self):
- input_file = os.path.join(self.SAMPLE_FILES, "")
- document = RasterisedDocumentParser(input_file)
- document._text = "lorem ipsum 13.02.2018 lorem ipsum"
- date = document.get_date()
- self.assertEqual(
- date,
- datetime.datetime(
- 2018, 2, 13, 0, 0,
- tzinfo=tz.gettz(settings.TIME_ZONE)
- )
- )
-
- @mock.patch(MOCK_SCRATCH, SCRATCH)
- def test_date_format_5(self):
- input_file = os.path.join(self.SAMPLE_FILES, "")
- document = RasterisedDocumentParser(input_file)
- document._text = (
- "lorem ipsum 130218, 2018, 20180213 and lorem 13.02.2018 lorem "
- "ipsum"
- )
- date = document.get_date()
- self.assertEqual(
- date,
- datetime.datetime(
- 2018, 2, 13, 0, 0,
- tzinfo=tz.gettz(settings.TIME_ZONE)
- )
- )
-
- @mock.patch(MOCK_SCRATCH, SCRATCH)
- def test_date_format_6(self):
- input_file = os.path.join(self.SAMPLE_FILES, "")
- document = RasterisedDocumentParser(input_file)
- document._text = (
- "lorem ipsum\n"
- "Wohnort\n"
- "3100\n"
- "IBAN\n"
- "AT87 4534\n"
- "1234\n"
- "1234 5678\n"
- "BIC\n"
- "lorem ipsum"
- )
- self.assertEqual(document.get_date(), None)
-
- @mock.patch(MOCK_SCRATCH, SCRATCH)
- def test_date_format_7(self):
- input_file = os.path.join(self.SAMPLE_FILES, "")
- document = RasterisedDocumentParser(input_file)
- document._text = (
- "lorem ipsum\n"
- "März 2019\n"
- "lorem ipsum"
- )
- date = document.get_date()
- self.assertEqual(
- date,
- datetime.datetime(
- 2019, 3, 1, 0, 0,
- tzinfo=tz.gettz(settings.TIME_ZONE)
- )
- )
-
- @mock.patch(MOCK_SCRATCH, SCRATCH)
- def test_date_format_8(self):
- input_file = os.path.join(self.SAMPLE_FILES, "")
- document = RasterisedDocumentParser(input_file)
- document._text = (
- "lorem ipsum\n"
- "Wohnort\n"
- "3100\n"
- "IBAN\n"
- "AT87 4534\n"
- "1234\n"
- "1234 5678\n"
- "BIC\n"
- "lorem ipsum\n"
- "März 2020"
- )
- self.assertEqual(
- document.get_date(),
- datetime.datetime(
- 2020, 3, 1, 0, 0,
- tzinfo=tz.gettz(settings.TIME_ZONE)
- )
- )
-
- @mock.patch(MOCK_SCRATCH, SCRATCH)
- def test_date_format_9(self):
- input_file = os.path.join(self.SAMPLE_FILES, "")
- document = RasterisedDocumentParser(input_file)
- document._text = (
- "lorem ipsum\n"
- "27. Nullmonth 2020\n"
- "März 2020\n"
- "lorem ipsum"
- )
- self.assertEqual(
- document.get_date(),
- datetime.datetime(
- 2020, 3, 1, 0, 0,
- tzinfo=tz.gettz(settings.TIME_ZONE)
- )
- )
-
- @mock.patch(
- "paperless_tesseract.parsers.RasterisedDocumentParser.get_text",
- return_value="01-07-0590 00:00:00"
- )
- @mock.patch(MOCK_SCRATCH, SCRATCH)
- def test_crazy_date_past(self, *args):
- document = RasterisedDocumentParser("/dev/null")
- document.get_text()
- self.assertIsNone(document.get_date())
-
- @mock.patch(
- "paperless_tesseract.parsers.RasterisedDocumentParser.get_text",
- return_value="01-07-2350 00:00:00"
- )
- @mock.patch(MOCK_SCRATCH, SCRATCH)
- def test_crazy_date_future(self, *args):
- document = RasterisedDocumentParser("/dev/null")
- document.get_text()
- self.assertIsNone(document.get_date())
-
- @mock.patch(
- "paperless_tesseract.parsers.RasterisedDocumentParser.get_text",
- return_value="20 408000l 2475"
- )
- @mock.patch(MOCK_SCRATCH, SCRATCH)
- def test_crazy_date_with_spaces(self, *args):
- document = RasterisedDocumentParser("/dev/null")
- document.get_text()
- self.assertIsNone(document.get_date())
-
- @mock.patch(
- "paperless_tesseract.parsers.RasterisedDocumentParser.get_text",
- return_value="No date in here"
- )
- @mock.patch(
- "paperless_tesseract.parsers.RasterisedDocumentParser."
- "FILENAME_DATE_ORDER",
- new_callable=mock.PropertyMock,
- return_value="YMD"
- )
- @mock.patch(MOCK_SCRATCH, SCRATCH)
- def test_filename_date_parse_invalid(self, *args):
- document = RasterisedDocumentParser("/tmp/20 408000l 2475 - test.pdf")
- document.get_text()
- self.assertIsNone(document.get_date())
diff --git a/src/paperless_tesseract/tests/test_ocr.py b/src/paperless_tesseract/tests/test_ocr.py
deleted file mode 100644
index 68ab64707..000000000
--- a/src/paperless_tesseract/tests/test_ocr.py
+++ /dev/null
@@ -1,80 +0,0 @@
-import os
-from unittest import mock, skipIf
-
-import pyocr
-from django.test import TestCase
-from pyocr.libtesseract.tesseract_raw import \
- TesseractError as OtherTesseractError
-
-from ..parsers import image_to_string, strip_excess_whitespace
-
-
-class FakeTesseract(object):
-
- @staticmethod
- def can_detect_orientation():
- return True
-
- @staticmethod
- def detect_orientation(file_handle, lang):
- raise OtherTesseractError("arbitrary status", "message")
-
- @staticmethod
- def image_to_string(file_handle, lang):
- return "This is test text"
-
-
-class FakePyOcr(object):
-
- @staticmethod
- def get_available_tools():
- return [FakeTesseract]
-
-
-class TestOCR(TestCase):
-
- text_cases = [
- ("simple string", "simple string"),
- (
- "simple newline\n testing string",
- "simple newline\ntesting string"
- ),
- (
- "utf-8 строка с пробелами в конце ",
- "utf-8 строка с пробелами в конце"
- )
- ]
-
- SAMPLE_FILES = os.path.join(os.path.dirname(__file__), "samples")
- TESSERACT_INSTALLED = bool(pyocr.get_available_tools())
-
- def test_strip_excess_whitespace(self):
- for source, result in self.text_cases:
- actual_result = strip_excess_whitespace(source)
- self.assertEqual(
- result,
- actual_result,
- "strip_exceess_whitespace({}) != '{}', but '{}'".format(
- source,
- result,
- actual_result
- )
- )
-
- @skipIf(not TESSERACT_INSTALLED, "Tesseract not installed. Skipping")
- @mock.patch(
- "paperless_tesseract.parsers.RasterisedDocumentParser.SCRATCH",
- SAMPLE_FILES
- )
- @mock.patch("paperless_tesseract.parsers.pyocr", FakePyOcr)
- def test_image_to_string_with_text_free_page(self):
- """
- This test is sort of silly, since it's really just reproducing an odd
- exception thrown by pyocr when it encounters a page with no text.
- Actually running this test against an installation of Tesseract results
- in a segmentation fault rooted somewhere deep inside pyocr where I
- don't care to dig. Regardless, if you run the consumer normally,
- text-free pages are now handled correctly so long as we work around
- this weird exception.
- """
- image_to_string(["no-text.png", "en"])
diff --git a/src/paperless_tesseract/tests/test_parser.py b/src/paperless_tesseract/tests/test_parser.py
new file mode 100644
index 000000000..7be176663
--- /dev/null
+++ b/src/paperless_tesseract/tests/test_parser.py
@@ -0,0 +1,292 @@
+import os
+import uuid
+from typing import ContextManager
+from unittest import mock
+
+from django.test import TestCase, override_settings
+
+from documents.parsers import ParseError, run_convert
+from documents.tests.utils import DirectoriesMixin
+from paperless_tesseract.parsers import RasterisedDocumentParser, get_text_from_pdf, strip_excess_whitespace
+
+image_to_string_calls = []
+
+
+def fake_convert(input_file, output_file, **kwargs):
+ with open(input_file) as f:
+ lines = f.readlines()
+
+ for i, line in enumerate(lines):
+ with open(output_file % i, "w") as f2:
+ f2.write(line.strip())
+
+
+class FakeImageFile(ContextManager):
+ def __init__(self, fname):
+ self.fname = fname
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ pass
+
+ def __enter__(self):
+ return os.path.basename(self.fname)
+
+
+
+
+class TestParser(DirectoriesMixin, TestCase):
+
+ def assertContainsStrings(self, content, strings):
+ # Asserts that all strings appear in content, in the given order.
+ indices = [content.index(s) for s in strings]
+ self.assertListEqual(indices, sorted(indices))
+
+ text_cases = [
+ ("simple string", "simple string"),
+ (
+ "simple newline\n testing string",
+ "simple newline\ntesting string"
+ ),
+ (
+ "utf-8 строка с пробелами в конце ",
+ "utf-8 строка с пробелами в конце"
+ )
+ ]
+
+ def test_strip_excess_whitespace(self):
+ for source, result in self.text_cases:
+ actual_result = strip_excess_whitespace(source)
+ self.assertEqual(
+ result,
+ actual_result,
+ "strip_exceess_whitespace({}) != '{}', but '{}'".format(
+ source,
+ result,
+ actual_result
+ )
+ )
+
+ SAMPLE_FILES = os.path.join(os.path.dirname(__file__), "samples")
+
+ def test_get_text_from_pdf(self):
+ text = get_text_from_pdf(os.path.join(self.SAMPLE_FILES, 'simple-digital.pdf'))
+
+ self.assertContainsStrings(text.strip(), ["This is a test document."])
+
+ def test_thumbnail(self):
+ parser = RasterisedDocumentParser(uuid.uuid4())
+ parser.get_thumbnail(os.path.join(self.SAMPLE_FILES, 'simple-digital.pdf'), "application/pdf")
+ # dont really know how to test it, just call it and assert that it does not raise anything.
+
+ @mock.patch("paperless_tesseract.parsers.run_convert")
+ def test_thumbnail_fallback(self, m):
+
+ def call_convert(input_file, output_file, **kwargs):
+ if ".pdf" in input_file:
+ raise ParseError("Does not compute.")
+ else:
+ run_convert(input_file=input_file, output_file=output_file, **kwargs)
+
+ m.side_effect = call_convert
+
+ parser = RasterisedDocumentParser(uuid.uuid4())
+ parser.get_thumbnail(os.path.join(self.SAMPLE_FILES, 'simple-digital.pdf'), "application/pdf")
+ # dont really know how to test it, just call it and assert that it does not raise anything.
+
+ def test_get_dpi(self):
+ parser = RasterisedDocumentParser(None)
+
+ dpi = parser.get_dpi(os.path.join(self.SAMPLE_FILES, "simple-no-dpi.png"))
+ self.assertEqual(dpi, None)
+
+ dpi = parser.get_dpi(os.path.join(self.SAMPLE_FILES, "simple.png"))
+ self.assertEqual(dpi, 72)
+
+ def test_simple_digital(self):
+ parser = RasterisedDocumentParser(None)
+
+ parser.parse(os.path.join(self.SAMPLE_FILES, "simple-digital.pdf"), "application/pdf")
+
+ self.assertTrue(os.path.isfile(parser.archive_path))
+
+ self.assertContainsStrings(parser.get_text(), ["This is a test document."])
+
+ def test_with_form(self):
+ parser = RasterisedDocumentParser(None)
+
+ parser.parse(os.path.join(self.SAMPLE_FILES, "with-form.pdf"), "application/pdf")
+
+ self.assertTrue(os.path.isfile(parser.archive_path))
+
+ self.assertContainsStrings(parser.get_text(), ["Please enter your name in here:", "This is a PDF document with a form."])
+
+ @override_settings(OCR_MODE="redo")
+ def test_with_form_error(self):
+ parser = RasterisedDocumentParser(None)
+
+ parser.parse(os.path.join(self.SAMPLE_FILES, "with-form.pdf"), "application/pdf")
+
+ self.assertIsNone(parser.archive_path)
+ self.assertContainsStrings(parser.get_text(), ["Please enter your name in here:", "This is a PDF document with a form."])
+
+ @override_settings(OCR_MODE="redo")
+ @mock.patch("paperless_tesseract.parsers.get_text_from_pdf", lambda _: None)
+ def test_with_form_error_notext(self):
+ parser = RasterisedDocumentParser(None)
+
+ def f():
+ parser.parse(os.path.join(self.SAMPLE_FILES, "with-form.pdf"), "application/pdf")
+
+ self.assertRaises(ParseError, f)
+
+ @override_settings(OCR_MODE="force")
+ def test_with_form_force(self):
+ parser = RasterisedDocumentParser(None)
+
+ parser.parse(os.path.join(self.SAMPLE_FILES, "with-form.pdf"), "application/pdf")
+
+ self.assertContainsStrings(parser.get_text(), ["Please enter your name in here:", "This is a PDF document with a form."])
+
+ def test_image_simple(self):
+ parser = RasterisedDocumentParser(None)
+
+ parser.parse(os.path.join(self.SAMPLE_FILES, "simple.png"), "image/png")
+
+ self.assertTrue(os.path.isfile(parser.archive_path))
+
+ self.assertContainsStrings(parser.get_text(), ["This is a test document."])
+
+ def test_image_simple_alpha_fail(self):
+ parser = RasterisedDocumentParser(None)
+
+ def f():
+ parser.parse(os.path.join(self.SAMPLE_FILES, "simple-alpha.png"), "image/png")
+
+ self.assertRaises(ParseError, f)
+
+ @mock.patch("paperless_tesseract.parsers.ocrmypdf.ocr")
+ def test_image_calc_a4_dpi(self, m):
+ parser = RasterisedDocumentParser(None)
+
+ parser.parse(os.path.join(self.SAMPLE_FILES, "simple-no-dpi.png"), "image/png")
+
+ m.assert_called_once()
+
+ args, kwargs = m.call_args
+
+ self.assertEqual(kwargs['image_dpi'], 62)
+
+ @mock.patch("paperless_tesseract.parsers.RasterisedDocumentParser.calculate_a4_dpi")
+ def test_image_dpi_fail(self, m):
+ m.return_value = None
+ parser = RasterisedDocumentParser(None)
+
+ def f():
+ parser.parse(os.path.join(self.SAMPLE_FILES, "simple-no-dpi.png"), "image/png")
+
+ self.assertRaises(ParseError, f)
+
+ @override_settings(OCR_IMAGE_DPI=72)
+ def test_image_no_dpi_default(self):
+ parser = RasterisedDocumentParser(None)
+
+ parser.parse(os.path.join(self.SAMPLE_FILES, "simple-no-dpi.png"), "image/png")
+
+ self.assertTrue(os.path.isfile(parser.archive_path))
+
+ self.assertContainsStrings(parser.get_text().lower(), ["this is a test document."])
+
+ def test_multi_page(self):
+ parser = RasterisedDocumentParser(None)
+ parser.parse(os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"), "application/pdf")
+ self.assertTrue(os.path.isfile(parser.archive_path))
+ self.assertContainsStrings(parser.get_text().lower(), ["page 1", "page 2", "page 3"])
+
+ @override_settings(OCR_PAGES=2, OCR_MODE="skip")
+ def test_multi_page_pages_skip(self):
+ parser = RasterisedDocumentParser(None)
+ parser.parse(os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"), "application/pdf")
+ self.assertTrue(os.path.isfile(parser.archive_path))
+ self.assertContainsStrings(parser.get_text().lower(), ["page 1", "page 2", "page 3"])
+
+ @override_settings(OCR_PAGES=2, OCR_MODE="redo")
+ def test_multi_page_pages_redo(self):
+ parser = RasterisedDocumentParser(None)
+ parser.parse(os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"), "application/pdf")
+ self.assertTrue(os.path.isfile(parser.archive_path))
+ self.assertContainsStrings(parser.get_text().lower(), ["page 1", "page 2", "page 3"])
+
+ @override_settings(OCR_PAGES=2, OCR_MODE="force")
+ def test_multi_page_pages_force(self):
+ parser = RasterisedDocumentParser(None)
+ parser.parse(os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"), "application/pdf")
+ self.assertTrue(os.path.isfile(parser.archive_path))
+ self.assertContainsStrings(parser.get_text().lower(), ["page 1", "page 2", "page 3"])
+
+ @override_settings(OOCR_MODE="skip")
+ def test_multi_page_analog_pages_skip(self):
+ parser = RasterisedDocumentParser(None)
+ parser.parse(os.path.join(self.SAMPLE_FILES, "multi-page-images.pdf"), "application/pdf")
+ self.assertTrue(os.path.isfile(parser.archive_path))
+ self.assertContainsStrings(parser.get_text().lower(), ["page 1", "page 2", "page 3"])
+
+ @override_settings(OCR_PAGES=2, OCR_MODE="redo")
+ def test_multi_page_analog_pages_redo(self):
+ parser = RasterisedDocumentParser(None)
+ parser.parse(os.path.join(self.SAMPLE_FILES, "multi-page-images.pdf"), "application/pdf")
+ self.assertTrue(os.path.isfile(parser.archive_path))
+ self.assertContainsStrings(parser.get_text().lower(), ["page 1", "page 2"])
+ self.assertFalse("page 3" in parser.get_text().lower())
+
+ @override_settings(OCR_PAGES=1, OCR_MODE="force")
+ def test_multi_page_analog_pages_force(self):
+ parser = RasterisedDocumentParser(None)
+ parser.parse(os.path.join(self.SAMPLE_FILES, "multi-page-images.pdf"), "application/pdf")
+ self.assertTrue(os.path.isfile(parser.archive_path))
+ self.assertContainsStrings(parser.get_text().lower(), ["page 1"])
+ self.assertFalse("page 2" in parser.get_text().lower())
+ self.assertFalse("page 3" in parser.get_text().lower())
+
+ @override_settings(OCR_MODE="skip_noarchive")
+ def test_skip_noarchive_withtext(self):
+ parser = RasterisedDocumentParser(None)
+ parser.parse(os.path.join(self.SAMPLE_FILES, "multi-page-digital.pdf"), "application/pdf")
+ self.assertIsNone(parser.archive_path)
+ self.assertContainsStrings(parser.get_text().lower(), ["page 1", "page 2", "page 3"])
+
+ @override_settings(OCR_MODE="skip_noarchive")
+ def test_skip_noarchive_notext(self):
+ parser = RasterisedDocumentParser(None)
+ parser.parse(os.path.join(self.SAMPLE_FILES, "multi-page-images.pdf"), "application/pdf")
+ self.assertTrue(os.path.join(parser.archive_path))
+ self.assertContainsStrings(parser.get_text().lower(), ["page 1", "page 2", "page 3"])
+
+
+class TestParserFileTypes(DirectoriesMixin, TestCase):
+
+ SAMPLE_FILES = os.path.join(os.path.dirname(__file__), "samples")
+
+ def test_bmp(self):
+ parser = RasterisedDocumentParser(None)
+ parser.parse(os.path.join(self.SAMPLE_FILES, "simple.bmp"), "image/bmp")
+ self.assertTrue(os.path.isfile(parser.archive_path))
+ self.assertTrue("this is a test document" in parser.get_text().lower())
+
+ def test_jpg(self):
+ parser = RasterisedDocumentParser(None)
+ parser.parse(os.path.join(self.SAMPLE_FILES, "simple.jpg"), "image/jpeg")
+ self.assertTrue(os.path.isfile(parser.archive_path))
+ self.assertTrue("this is a test document" in parser.get_text().lower())
+
+ @override_settings(OCR_IMAGE_DPI=200)
+ def test_gif(self):
+ parser = RasterisedDocumentParser(None)
+ parser.parse(os.path.join(self.SAMPLE_FILES, "simple.gif"), "image/gif")
+ self.assertTrue(os.path.isfile(parser.archive_path))
+ self.assertTrue("this is a test document" in parser.get_text().lower())
+
+ def test_tiff(self):
+ parser = RasterisedDocumentParser(None)
+ parser.parse(os.path.join(self.SAMPLE_FILES, "simple.tif"), "image/tiff")
+ self.assertTrue(os.path.isfile(parser.archive_path))
+ self.assertTrue("this is a test document" in parser.get_text().lower())
diff --git a/src/paperless_tesseract/tests/test_signals.py b/src/paperless_tesseract/tests/test_signals.py
deleted file mode 100644
index b5ff4da59..000000000
--- a/src/paperless_tesseract/tests/test_signals.py
+++ /dev/null
@@ -1,36 +0,0 @@
-from django.test import TestCase
-
-from ..signals import ConsumerDeclaration
-
-
-class SignalsTestCase(TestCase):
-
- def test_test_handles_various_file_names_true(self):
-
- prefixes = (
- "doc", "My Document", "Μυ Γρεεκ Δοψθμεντ", "Doc -with - tags",
- "A document with a . in it", "Doc with -- in it"
- )
- suffixes = (
- "pdf", "jpg", "jpeg", "gif", "png", "tiff", "tif", "pnm", "bmp",
- "PDF", "JPG", "JPEG", "GIF", "PNG", "TIFF", "TIF", "PNM", "BMP",
- "pDf", "jPg", "jpEg", "gIf", "pNg", "tIff", "tIf", "pNm", "bMp",
- )
-
- for prefix in prefixes:
- for suffix in suffixes:
- name = "{}.{}".format(prefix, suffix)
- self.assertTrue(ConsumerDeclaration.test(name))
-
- def test_test_handles_various_file_names_false(self):
-
- prefixes = ("doc",)
- suffixes = ("txt", "markdown", "",)
-
- for prefix in prefixes:
- for suffix in suffixes:
- name = "{}.{}".format(prefix, suffix)
- self.assertFalse(ConsumerDeclaration.test(name))
-
- self.assertFalse(ConsumerDeclaration.test(""))
- self.assertFalse(ConsumerDeclaration.test("doc"))
diff --git a/src/paperless_text/apps.py b/src/paperless_text/apps.py
index 389167368..1acc361aa 100644
--- a/src/paperless_text/apps.py
+++ b/src/paperless_text/apps.py
@@ -1,5 +1,7 @@
from django.apps import AppConfig
+from paperless_text.signals import text_consumer_declaration
+
class PaperlessTextConfig(AppConfig):
@@ -9,8 +11,6 @@ class PaperlessTextConfig(AppConfig):
from documents.signals import document_consumer_declaration
- from .signals import ConsumerDeclaration
-
- document_consumer_declaration.connect(ConsumerDeclaration.handle)
+ document_consumer_declaration.connect(text_consumer_declaration)
AppConfig.ready(self)
diff --git a/src/paperless_text/parsers.py b/src/paperless_text/parsers.py
index 3ccb78404..7e488ca37 100644
--- a/src/paperless_text/parsers.py
+++ b/src/paperless_text/parsers.py
@@ -1,6 +1,7 @@
import os
import subprocess
+from PIL import ImageDraw, ImageFont, Image
from django.conf import settings
from documents.parsers import DocumentParser, ParseError
@@ -11,86 +12,29 @@ class TextDocumentParser(DocumentParser):
This parser directly parses a text document (.txt, .md, or .csv)
"""
- CONVERT = settings.CONVERT_BINARY
- THREADS = int(settings.OCR_THREADS) if settings.OCR_THREADS else None
- UNPAPER = settings.UNPAPER_BINARY
- DEFAULT_OCR_LANGUAGE = settings.OCR_LANGUAGE
- OCR_ALWAYS = settings.OCR_ALWAYS
-
- def __init__(self, path):
- super().__init__(path)
- self._text = None
-
- def get_thumbnail(self):
- """
- The thumbnail of a text file is just a 500px wide image of the text
- rendered onto a letter-sized page.
- """
- # The below is heavily cribbed from https://askubuntu.com/a/590951
-
- bg_color = "white" # bg color
- text_color = "black" # text color
- psize = [500, 647] # icon size
- n_lines = 50 # number of lines to show
- out_path = os.path.join(self.tempdir, "convert.png")
-
- temp_bg = os.path.join(self.tempdir, "bg.png")
- temp_txlayer = os.path.join(self.tempdir, "tx.png")
- picsize = "x".join([str(n) for n in psize])
- txsize = "x".join([str(n - 8) for n in psize])
-
- def create_bg():
- work_size = ",".join([str(n - 1) for n in psize])
- r = str(round(psize[0] / 10))
- rounded = ",".join([r, r])
- run_command(
- self.CONVERT,
- "-size ", picsize,
- ' xc:none -draw ',
- '"fill ', bg_color, ' roundrectangle 0,0,', work_size, ",", rounded, '" ', # NOQA: E501
- temp_bg
- )
+ def get_thumbnail(self, document_path, mime_type):
def read_text():
- with open(self.document_path, 'r') as src:
- lines = [l.strip() for l in src.readlines()]
- text = "\n".join([l for l in lines[:n_lines]])
- return text.replace('"', "'")
+ with open(document_path, 'r') as src:
+ lines = [line.strip() for line in src.readlines()]
+ text = "\n".join(lines[:50])
+ return text
- def create_txlayer():
- run_command(
- self.CONVERT,
- "-background none",
- "-fill",
- text_color,
- "-pointsize", "12",
- "-border 4 -bordercolor none",
- "-size ", txsize,
- ' caption:"', read_text(), '" ',
- temp_txlayer
- )
+ img = Image.new("RGB", (500, 700), color="white")
+ draw = ImageDraw.Draw(img)
+ font = ImageFont.truetype(
+ "/usr/share/fonts/liberation/LiberationSerif-Regular.ttf", 20,
+ layout_engine=ImageFont.LAYOUT_BASIC)
+ draw.text((5, 5), read_text(), font=font, fill="black")
- create_txlayer()
- create_bg()
- run_command(
- self.CONVERT,
- temp_bg,
- temp_txlayer,
- "-background None -layers merge ",
- out_path
- )
+ out_path = os.path.join(self.tempdir, "thumb.png")
+ img.save(out_path)
return out_path
- def get_text(self):
-
- if self._text is not None:
- return self._text
-
- with open(self.document_path, 'r') as f:
- self._text = f.read()
-
- return self._text
+ def parse(self, document_path, mime_type):
+ with open(document_path, 'r') as f:
+ self.text = f.read()
def run_command(*args):
diff --git a/src/paperless_text/signals.py b/src/paperless_text/signals.py
index ae5a005e1..1e0493f4f 100644
--- a/src/paperless_text/signals.py
+++ b/src/paperless_text/signals.py
@@ -1,23 +1,12 @@
-import re
-
from .parsers import TextDocumentParser
-class ConsumerDeclaration:
-
- MATCHING_FILES = re.compile(r"^.*\.(te?xt|md|csv)$")
-
- @classmethod
- def handle(cls, sender, **kwargs):
- return cls.test
-
- @classmethod
- def test(cls, doc):
-
- if cls.MATCHING_FILES.match(doc.lower()):
- return {
- "parser": TextDocumentParser,
- "weight": 10
- }
-
- return None
+def text_consumer_declaration(sender, **kwargs):
+ return {
+ "parser": TextDocumentParser,
+ "weight": 10,
+ "mime_types": {
+ "text/plain": ".txt",
+ "text/csv": ".csv",
+ }
+ }
diff --git a/src/paperless_text/tests/samples/test.txt b/src/paperless_text/tests/samples/test.txt
new file mode 100644
index 000000000..6de7b8c69
--- /dev/null
+++ b/src/paperless_text/tests/samples/test.txt
@@ -0,0 +1 @@
+This is a test file.
diff --git a/src/paperless_text/tests/test_parser.py b/src/paperless_text/tests/test_parser.py
new file mode 100644
index 000000000..413aa91cf
--- /dev/null
+++ b/src/paperless_text/tests/test_parser.py
@@ -0,0 +1,26 @@
+import os
+
+from django.test import TestCase
+
+from documents.tests.utils import DirectoriesMixin
+from paperless_text.parsers import TextDocumentParser
+
+
+class TestTextParser(DirectoriesMixin, TestCase):
+
+ def test_thumbnail(self):
+
+ parser = TextDocumentParser(None)
+
+ # just make sure that it does not crash
+ f = parser.get_thumbnail(os.path.join(os.path.dirname(__file__), "samples", "test.txt"), "text/plain")
+ self.assertTrue(os.path.isfile(f))
+
+ def test_parse(self):
+
+ parser = TextDocumentParser(None)
+
+ parser.parse(os.path.join(os.path.dirname(__file__), "samples", "test.txt"), "text/plain")
+
+ self.assertEqual(parser.get_text(), "This is a test file.\n")
+ self.assertIsNone(parser.get_archive_path())
diff --git a/src/reminders/admin.py b/src/reminders/admin.py
deleted file mode 100644
index bc39e24aa..000000000
--- a/src/reminders/admin.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from django.conf import settings
-from django.contrib import admin
-
-from .models import Reminder
-
-
-class ReminderAdmin(admin.ModelAdmin):
-
- class Media:
- css = {
- "all": ("paperless.css",)
- }
-
- list_per_page = settings.PAPERLESS_LIST_PER_PAGE
- list_display = ("date", "document", "note")
- list_filter = ("date",)
- list_editable = ("note",)
-
-
-admin.site.register(Reminder, ReminderAdmin)
diff --git a/src/reminders/apps.py b/src/reminders/apps.py
deleted file mode 100644
index a745cad29..000000000
--- a/src/reminders/apps.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from django.apps import AppConfig
-
-
-class RemindersConfig(AppConfig):
- name = "reminders"
diff --git a/src/reminders/filters.py b/src/reminders/filters.py
deleted file mode 100644
index 8ca8f4402..000000000
--- a/src/reminders/filters.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from django_filters.rest_framework import CharFilter, FilterSet
-
-from .models import Reminder
-
-
-class ReminderFilterSet(FilterSet):
-
- class Meta(object):
- model = Reminder
- fields = {
- "document": ["exact"],
- "date": ["gt", "lt", "gte", "lte", "exact"],
- "note": ["istartswith", "iendswith", "icontains"]
- }
diff --git a/src/reminders/migrations/0001_initial.py b/src/reminders/migrations/0001_initial.py
deleted file mode 100644
index 6daad4ef2..000000000
--- a/src/reminders/migrations/0001_initial.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10.5 on 2017-03-25 15:58
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ('documents', '0016_auto_20170325_1558'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='Reminder',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('date', models.DateTimeField()),
- ('note', models.TextField(blank=True)),
- ('document', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='documents.Document')),
- ],
- ),
- ]
diff --git a/src/reminders/migrations/0002_auto_20181007_1420.py b/src/reminders/migrations/0002_auto_20181007_1420.py
deleted file mode 100644
index 324764d2c..000000000
--- a/src/reminders/migrations/0002_auto_20181007_1420.py
+++ /dev/null
@@ -1,19 +0,0 @@
-# Generated by Django 2.0.8 on 2018-10-07 14:20
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('reminders', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='reminder',
- name='document',
- field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='documents.Document'),
- ),
- ]
diff --git a/src/reminders/models.py b/src/reminders/models.py
deleted file mode 100644
index e8ac7020b..000000000
--- a/src/reminders/models.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from django.db import models
-
-
-class Reminder(models.Model):
-
- document = models.ForeignKey(
- "documents.Document", on_delete=models.PROTECT)
- date = models.DateTimeField()
- note = models.TextField(blank=True)
diff --git a/src/reminders/serialisers.py b/src/reminders/serialisers.py
deleted file mode 100644
index bf8dd09c2..000000000
--- a/src/reminders/serialisers.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from documents.models import Document
-from rest_framework import serializers
-
-from .models import Reminder
-
-
-class ReminderSerializer(serializers.HyperlinkedModelSerializer):
-
- document = serializers.HyperlinkedRelatedField(
- view_name="drf:document-detail", queryset=Document.objects)
-
- class Meta(object):
- model = Reminder
- fields = ("id", "document", "date", "note")
diff --git a/src/reminders/tests.py b/src/reminders/tests.py
deleted file mode 100644
index 7ce503c2d..000000000
--- a/src/reminders/tests.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from django.test import TestCase
-
-# Create your tests here.
diff --git a/src/reminders/views.py b/src/reminders/views.py
deleted file mode 100644
index d2ead96b5..000000000
--- a/src/reminders/views.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from django_filters.rest_framework import DjangoFilterBackend
-from rest_framework.filters import OrderingFilter
-from rest_framework.permissions import IsAuthenticated
-from rest_framework.viewsets import (
- ModelViewSet,
-)
-
-from .filters import ReminderFilterSet
-from .models import Reminder
-from .serialisers import ReminderSerializer
-from paperless.views import StandardPagination
-
-
-class ReminderViewSet(ModelViewSet):
- model = Reminder
- queryset = Reminder.objects
- serializer_class = ReminderSerializer
- pagination_class = StandardPagination
- permission_classes = (IsAuthenticated,)
- filter_backends = (DjangoFilterBackend, OrderingFilter)
- filter_class = ReminderFilterSet
- ordering_fields = ("date", "document")
diff --git a/src/setup.cfg b/src/setup.cfg
index f9572519b..2a1a348bd 100644
--- a/src/setup.cfg
+++ b/src/setup.cfg
@@ -1,18 +1,15 @@
[pycodestyle]
-exclude = migrations, paperless/settings.py, .tox
-
+exclude = migrations, paperless/settings.py, .tox, */tests/*
[tool:pytest]
DJANGO_SETTINGS_MODULE=paperless.settings
-addopts = --pythonwarnings=all -n auto
+addopts = --pythonwarnings=all --cov --cov-report=html -n auto
env =
- PAPERLESS_PASSPHRASE=THISISNOTASECRET
- PAPERLESS_SECRET=paperless
- PAPERLESS_EMAIL_SECRET=paperless
+ PAPERLESS_DISABLE_DBHANDLER=true
[coverage:run]
source =
./
omit =
- */tests
+ */tests/*