[
  {
    "path": ".circleci/config.yml",
    "content": "version: 2.1\n\nexecutors:\n  docker-publisher:\n    environment:\n      IMAGE_NAME: bukuserver/bukuserver\n    docker:\n      - image: cimg/base:current\n\ntest-template: &test-template\n  working_directory: ~/Buku\n  environment:\n    CI_FORCE_TEST: 1\n  steps:\n    - run: &init\n        command: |\n          apt update && apt install -y --no-install-recommends git\n          pip install --upgrade pip\n    - checkout\n    - run: &deps\n        command: |\n          pip install -e . --group dev\n    - run:\n        command:\n          python3 -m pytest ./tests/test_*.py --cov buku -vv --durations=0 -c ./tests/pytest.ini\n\nlint-template: &lint-template\n  working_directory: ~/Buku\n  environment:\n    CI_FORCE_TEST: 1\n  steps:\n    - run: *init\n    - checkout\n    - run: *deps\n    - run:\n        command: |\n          python3 -m flake8\n          echo buku | xargs pylint --rcfile tests/.pylintrc\n          find . -iname \"*.py\" ! -path \"./api/*\" | xargs pylint --rcfile tests/.pylintrc\n\njobs:\n  lint:\n    docker:\n      - image: python:3.13-slim\n    <<: *lint-template\n\n  py310:\n    docker:\n      - image: python:3.10-slim\n    <<: *test-template\n\n  py311:\n    docker:\n      - image: python:3.11-slim\n    <<: *test-template\n\n  py312:\n    docker:\n      - image: python:3.12-slim\n    <<: *test-template\n\n  py313:\n    docker:\n      - image: python:3.13-slim\n    <<: *test-template\n\n  py314:\n    docker:\n      - image: python:3.14-slim\n    <<: *test-template\n\n#  package-and-publish:\n#    machine: true\n#    working_directory: ~/Buku\n#    steps:\n#      - checkout\n#      - run:\n#          name: \"package with packagecore\"\n#          command: |\n#            # Use latest installed python3 from pyenv\n#            export PYENV_VERSION=\"$(pyenv versions | grep -Po '\\b3\\.\\d+\\.\\d+' | tail -1)\"\n#            pip install packagecore\n#            packagecore -o ./dist/ ${CIRCLE_TAG#v}\n#      - run:\n#          name: \"publish to GitHub\"\n#          command: |\n#            go get github.com/tcnksm/ghr\n#            ghr -t ${GITHUB_API_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} -c ${CIRCLE_SHA1} -replace ${CIRCLE_TAG} ./dist/\n\n  build-and-publish-docker-image:\n    executor: docker-publisher\n    steps:\n      - checkout\n      - setup_remote_docker\n      - run:\n          name: Login to Docker Hub\n          command: 'echo \"$DOCKERHUB_PASS\" | docker login -u \"$DOCKERHUB_USERNAME\" --password-stdin'\n      - run:\n          name: Create a builder instance\n          command: docker buildx create --use\n      - run:\n          name: Build Docker image and publish it to Docker Hub\n          command: |\n            TAGS=\"--tag $IMAGE_NAME:latest\"\n            [ \"$CIRCLE_TAG\" ] && TAGS=\"--tag $IMAGE_NAME:$CIRCLE_TAG $TAGS\"\n            docker buildx build --platform=linux/amd64,linux/arm64 $TAGS --push .\n\nworkflows:\n  version: 2.1\n\n  CircleCI:\n    jobs: &all-tests\n      - lint\n      - py310\n      - py311\n      - py312\n      - py313\n      - py314\n\n  nightly:\n    triggers:\n      - schedule:\n          cron: \"0 0 * * 6\"\n          filters:\n            branches:\n              only:\n                - master\n    jobs: *all-tests\n\n#  publish-github-release:\n#    jobs:\n#      - package-and-publish:\n#          filters:\n#            tags:\n#              only: /^v.*/\n#            branches:\n#              ignore: /.*/\n\n  publish-docker-image:\n    jobs:\n      - build-and-publish-docker-image:\n          filters:\n            tags:\n              only: /^v.*/\n            branches:\n              ignore: /.*/\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "github: jarun\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "#### Bug reports\n\nBefore opening an issue, please try to reproduce on [the latest development version](https://github.com/jarun/Buku#from-source) first. The bug you noticed might have already been fixed.\n\nIf the issue can be reproduced on master, then please make sure you provide the following:\n- Debug logs using the `-z` option;\n- Details of operating system, Python version used, terminal emulator and shell;\n- `locale` output, if relevant. It's a good idea to set your locale to UFT-8. Please refer to [Buku #131](https://github.com/jarun/Buku/issues/30).\n\nIf we need more information and there is no communication from the bug reporter within 7 days from the date of request, we will close the issue. If you have relevant information, resume discussion any time.\n\n\n#### Feature requests\nPlease consider contributing the feature back to `Buku` yourself. Feel free to discuss. We are more than happy to help.\n\n--- PLEASE DELETE THIS LINE AND EVERYTHING ABOVE ---\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "Did you visit the [PR guidelines](https://github.com/jarun/Buku/wiki/PR-guidelines)?\n\n--- PLEASE DELETE THIS LINE AND EVERYTHING ABOVE ---\n"
  },
  {
    "path": ".github/workflows/lock.yml",
    "content": "name: 'Lock threads'\n\non:\n  schedule:\n    - cron: '0 0 * * *'\n  workflow_dispatch:\n\npermissions:\n  issues: write\n  pull-requests: write\n  discussions: write\n\nconcurrency:\n  group: lock-threads\n\njobs:\n  lock:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: dessant/lock-threads@v5\n        with:\n          github-token: ${{ github.token }}\n          issue-inactive-days: '30'\n          issue-lock-reason: ''\n          pr-inactive-days: '30'\n          pr-lock-reason: ''\n"
  },
  {
    "path": ".gitignore",
    "content": "*.py[co]\n*.sw[po]\n.cache/\n.coverage\n.hypothesis\nbuku.egg-info\ndist\nbuild\n.tox\n.history\n.vscode/launch.json\n/tests/test_bukuDb/places.sqlite*\n/tests/vcr_cassettes/test_search_and_open_all_in_browser.yaml\n/tests/vcr_cassettes/tests.test_bukuDb/\n/bookmarks.db\n/venv/\n/docker-compose/data/*\n!/docker-compose/data/nginx/\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "# See https://pre-commit.com for more information\n# See https://pre-commit.com/hooks.html for more hooks\nrepos:\n- repo: https://github.com/pre-commit/pre-commit-hooks\n  rev: v4.3.0\n  hooks:\n  - id: check-added-large-files\n- repo: https://github.com/pycqa/isort\n  rev: 5.10.1\n  hooks:\n  - id: isort\n    name: isort (python)\n    args: [--profile, black, --filter-files]\n- repo: https://github.com/macisamuele/language-formatters-pre-commit-hooks\n  rev: v2.4.0\n  hooks:\n  - id: pretty-format-yaml\n    args: [--autofix]\n- repo: https://github.com/akaihola/darker\n  rev: 1.5.1\n  hooks:\n  - id: darker\n    args: [--line-length, '139', --skip-string-normalization]\n- repo: https://github.com/PyCQA/pylint/\n  rev: v2.15.5\n  hooks:\n  - id: pylint\n    name: pylint\n    entry: pylint\n    language: system\n    types: [python]\n    args: [-rn, -sn, --rcfile=tests/.pylintrc]\n    # \"-rn\", # Only display messages\n    # \"-sn\", # Don't display the score\n    # based on\n    # https://pylint.pycqa.org/en/latest/user_guide/pre-commit-integration.html\n- repo: https://github.com/PyCQA/autoflake\n  rev: v1.7.7\n  hooks:\n  - id: autoflake\n"
  },
  {
    "path": ".readthedocs.yaml",
    "content": "# .readthedocs.yaml\n# Read the Docs configuration file\n# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details\n\n# Required\nversion: 2\n\n# Set the version of Python and other tools you might need\nbuild:\n  os: ubuntu-24.04\n  tools:\n    python: '3.12'\n  jobs:\n    install:\n      - pip install --upgrade pip\n      - pip install .[locales,server] --group docs\n\n# Build documentation in the docs/ directory with Sphinx\nsphinx:\n  configuration: docs/source/conf.py\n\n# If using Sphinx, optionally build your docs in additional formats such as PDF\n# formats:\n#    - pdf\n"
  },
  {
    "path": "CHANGELOG",
    "content": "buku (unreleased)\n\n- Bukuserver: mitigate Host header poisoning via BUKUSERVER_SERVER_NAME config\n\nbuku v5.1\n2025-12-07\n\n- auto-import from Brave Browser\n- `amd64` docker image (will be built starting from next release)\n- sorting bookmarks by absence/presence of a specific tag\n- reordering all bookmarks in DB (based on specified sorting)\n- support overriding default URI scheme for `browse()` (@meonkeys)\n- include a `Sec-Fetch-Mode` header when fetching a webpage (in case the website admin cares about that)\n- source: renaming main file to `buku.py` (which is the name it gets installed as)\n- packaging: migrating from `setup.py` to `pyproject.toml` (@branchvincent)\n- packaging: installing the manpage into `pipx` venv (@branchvincent)\n- Python: adding v3.14, removing v3.9\n- API: improved BukuCrypt\n- Bukuserver: Swagger-based interactive documentation for the web-API\n- Bukuserver: option to change initial value of Fetch checkbox in Create Bookmark form\n- Bukuserver: migrate from Bootstrap v3 to v4\n- migrate to the upcoming flask-admin v2 (see #753)\n\nbuku v5.0\n2025-04-37\n\n- removed URL shorten/expand feature (see #815)\n- fix \"htttp\" typos in tests (@chenrui333)\n- fix default value of `--url` used with `--update` (see #729)\n- add Python 3.12 support (@chenrui333)\n- add Python 3.13 support + use it as default for testing\n- list [BukuBot](https://git.xmpp-it.net/sch/BukuBot) in the docs as a related project (@sjehuda)\n- fix pylint warnings\n- fix parsing of `<meta name=\"keywords\">` in fetched webpages (see #734)\n- fix `get_rec_all_by_ids()`\n- fix autoimport of Firefox profiles with a custom absolute path (see #791)\n- fix `--db` parameter being ignored by encryption actions (see #796)\n- fix inconsistent handling of netloc (see #799)\n- fix import in single-parent-folder-tag mode (see #807)\n- add import/export in the `.rss` (Atom) format (@vagos)\n- add `.atom` as an RSS import/export extension\n- add description to XBEL export format (@sjehuda)\n- support raw (unnamed) URLs when parsing Markdown/OrgMode (i.e. `<url>`/`[[url]]` respectively)\n- implement backwards navigation in the interactive shell mode (@20mattia02)\n- implement DB switching in the interactive shell mode (with displaying non-default DB name)\n- support opening interactive shell mode on a non-default DB\n- implement custom-setting the default DB directory\n- implement printing DB list in default directory when invoked specifically as `buku --db`\n- implement handling `--db` parameter without `.` and directory separators as a _name_ (with omitted `.db` extension and located in default DB directory)\n- Bukuserver: implement support for passing a DB name in `BUKUSERVER_DB_FILE`\n- implement thread safety (e.g. for handling simultaneous requests in Bukuserver)\n- Bukuserver: prevent `/api/tags` query from being spammed due to a Flask-Admin bug\n- Bukuserver: increase size of the bookmarklet popup window\n- Bukuserver: minor UI improvements\n- Bukuserver: some readme improvements (@rachmadaniHaryono)\n- Bukuserver: add \"contains\"/\"not contains\" modes for some filters (URL/title in Bookmarks, name in Tags)\n- Bukuserver: fix input autofoxus on page/dialog opening\n- Bukuserver: ensure that links in the bookmarklet popup window are always opened externally\n- allow tags supplied by `--add` (or by `--tag`) to override fetched tags (unless started with `+`)\n- implement search-with-markers (see #777)\n- implement bookmarks output ordering [by fields/netloc] (see #777)\n- implement random selection of bookmarks\n- implement bookmark index swapping\n- Bukuserver: implement translations\n- Bukuserver: fix unstable ordering of the filters form\n- Bukuserver: add a runner script\n- Bukuserver: add support for tags/fetch fields to bookmarklet API endpoint\n- Bukuserver: fix popup detection for sites that prevent exposing `opener`\n- Bukuserver: fix disabling no-netloc URLs in rendering modes other than `full`\n- Bukuserver: remove dependency on the stale package Flask-API\n- support `urllib3>=2` as a dependency (see #776)\n- fix various deprecation warnings\n- Bukuserver: update screenshots in documentation\n\n-------------------------------------------------------------------------------\n\nbuku v4.9\n2024-04-07\n\n- fixed profile detection for multiple Firefox installs (#711)\n- added option `--offline` to add a bookmark without web connection\n- added a mini-guide for quick keyboard access to the bookmarklet\n- support environment variable `NO_COLOR`\n- fixed HTML encoding detection (#713)\n- fixed Windows profile detection (#683)\n- support python 3.11 (support for python 3.7 removed)\n- fixed readline internal error on Windows (#704)\n\n-------------------------------------------------------------------------------\n\nbuku v4.8\n2023-02-18\n\n- support Vivaldi browser\n- better XBEL compatibility\n- check for empty search results in piped operations\n- remove python 3.6 support, add 3.10\n- API changes in bukudb (#660):\n  - bookmark data tuples returned from methods `get_rec_all()`\n    & `get_rec_by_id()`, now have user-friendly properties\n    (`id`, `url`, `title`, `desc`, `tags`/`taglist`, `immutable`;\n    as well as for raw DB fields – `tags_raw`, `flags`)\n  - methods `get_rec_all()`, `list_using_id()`, `searchdb()`, `search_by_tag()`,\n    `search_keywords_and_filter_by_tags()` & `exclude_results_from_search()`\n    are now guaranteed to return a list (empty if no data is found)\n  - methods `get_rec_id()`, `get_max_id()` & `add_rec()` now return `None` as\n    the \"no ID\" value\n  - methods `add_rec()`, `update_rec()` & `edit_update_rec()` now treat the\n    value of `immutable` parameter as a boolean (the default/noop value for\n    update calls is `None`)\n  - a `FIELD_FILTER` dictionary is introduced that contains fields formatting\n    description; also, in `format_json()` (and `print_json_safe()`), the output\n    format now matches the one described in CLI help\n- IMPACT: If you have a local repo clone, remove .tox/ subfolder if it's there\n    before you run tests for the first time\n\n-------------------------------------------------------------------------------\n\nbuku v4.7\n2022-07-01\n\n- support XBEL export/import (#569)\n- support for Microsoft Edge bookmarks (#585)\n- block web fetch on import\n- many bukuserver fixes (#543, #545, #547, #548, #553, #554, #559)\n- import nested directory names as tags on html import (#539)\n- fix slow/failed markdown import (#538)\n- fix SSL certificate identification not working on macOS (#528)\n- import tags from markdown (#523)\n- fix broken pipe error with oil (#520)\n\n-------------------------------------------------------------------------------\n\nbuku v4.6\n2021-06-16\n\n- use textwrap to wrap comments and tags when printing in terminal\n- show listing start and end index over prompt in interactive mode\n- option `--nostdin`: don't wait for input (must be first arg) (#513)\n- user-friendly prompt message when watiing for input in non-tty mode\n- several test framework improvements\n\n-------------------------------------------------------------------------------\n\nbuku v4.5\n2020-12-29\n\n- Fix encryption and decryption (#480)\n- Fix Wayback Machine API query\n- Support wayland native copier `wl-copy`\n- Add bookmarklet for bukuserver (#385)\n- Delete by tag without prompting for each bookmark (#458)\n- Fix issue with utf-8 characters in bookmark titles (#456)\n- Fix reomve all tags from prompt (#467)\n- Example to fuzzy search and preview in Quickstart section\n- Replace debug option `-z` with `-g`\n- Support python 3.9, retire python 3.5\n\n-------------------------------------------------------------------------------\n\nbuku v4.4\n2020-06-16\n\n- optionally specify output file with `--json`\n- confirm auto-tag generation in chatty mode\n- unblock GUI browsers when running on WSL\n- handle up to 10 server redirects (#452)\n- fix issue with reverse proxy (#435)\n- use ImportError instead ModuleNotFoundError (#437)\n- import pyreadline on windows (#441)\n- auto-generated package refresh\n\n-------------------------------------------------------------------------------\n\nBuku v4.3\n2020-01-31\n\n- Project renamed to `buku` (small `b`)\n- Export tags in markdown format as comments\n- Tag support for Org import/export\n- Better Windows 10 support\n- Reverse proxy support for `bukuserver`\n- Config `OPEN_IN_NEW_TAB` for `bukuserver`\n- Documentation updated\n- Fix Firefox default profile detection\n- Fix export to DB failing after search\n- Fix broken prompt colors\n- User agent updated\n\n-------------------------------------------------------------------------------\n\nBuku v4.2.2\n2019-05-02\n\n- Fixes broken prompt due to PR #373\n\n-------------------------------------------------------------------------------\n\nBuku v4.2.1\n2019-04-30\n\n- A fix on top of v4.2 to address a packaging problem\n\n-------------------------------------------------------------------------------\n\nBuku v4.2\n2019-04-30\n\n- Disabled appending tags from page on update\n- Improved Windows color support using colorama (optional dep)\n- New format option to show only title and tag\n- Python 3.4 is EOL, support discontinued\n- Several fixes and code refactor\n\n-------------------------------------------------------------------------------\n\nBuku v4.1\n2019-01-15\n\nWhat's in?\n- Import firefox-exported json\n- Fix auto-import for firefox\n- Fix write to GNU Screen paste buffer\n- Some CVE fixes\n\n-------------------------------------------------------------------------------\n\nBuku v4.0\n2018-11-01\n\nWhat's in?\n- Show records in pages with option `-p` (works with `-n`, default 10)\n- Enhanced clipboard support: `xclip`, tmux, GNU Screen, Termux\n- Prompt key `O` works with search results along with GUI browser toggling\n- Search by taglist id with prompt key `g`\n- Multiple fixes\n\n-------------------------------------------------------------------------------\n\nBuku v3.9\n2018-08-30\n\nWhat's in?\n- Set number of search results per page (default 10)\n- Retrieve description and tags from page, if available\n- Visit cached version on Wayback Machine\n- Export works with all search options now\n- Changed user agent to Firefox on Ubuntu\n- Several dependencies made _required_ for installation\n- bukuserver will use Flask-Admin\n\n-------------------------------------------------------------------------------\n\nBuku v3.8\n2018-05-24\n\nWhat's in?\n- A self-hosted http server, bukuserver, that exposes core functionality\n    - browsable front-end on a local web host server\n    - flask default cli interface is used instead custom one\n    - handle not only API but also HTML request\n    - statistic page\n    - CRUD on bookmark\n    - replaces the earlier API module\n- Import complete folder hierarchy as tags during auto-import\n- Merge tags on import even if bookmark URL exists\n- Orgfile import/export\n- Show bookmarks to be deleted before deletion\n- Merge tags during import if bookmark exists\n- Escape regex metacharacters in regex input\n\n-------------------------------------------------------------------------------\n\nBuku v3.7\n2018-03-28\n\nWhat's in?\n- Exclude keywords in search (keyword filtering)\n- Search and filter by tags\n- Order search results by number of keyword matches\n- Copy URL to clipboard\n- Prompt shortcut 'O' to override text browsers\n- New official packagers: Fedora, Gentoo, OpenBSD, openSUSE\n\n-------------------------------------------------------------------------------\n\nBuku v3.6\n2018-01-09\n\nWhat's in?\n- Skip bookmark addition if edit is aborted\n- Use urllib3 for handling http connections everywhere\n- Fix auto-import on FreeBSD\n- Generate packages for openSUSE Leap 42.3, Fedora 27\n\n-------------------------------------------------------------------------------\n\nBuku v3.5\n2017-11-10\n\nWhat's in?\n- Buku now has its own user agent\n- Search works with field filters\n- Edit the last record with `-w=-1` (useful when adding bookmark from GUI)a\n- Support for Chromium browser\n- Colors disabled by default on cmd (Windows), option `--colors` has to be used\n- Get default Firefox profile name from profiles.ini\n- Bash scriptlet to autogen records for testing\n- Some optimization in add record and suggest tags\n- A fresh utility Pinku to import Pinboard bookmarks to Buku\n\n-------------------------------------------------------------------------------\n\nBuku v3.4\n2017-09-18\n\nWhat's in?\n- Export bookmarks (including specific tags) to Buku DB file using `--export`\n- Option `--import` can merge Buku DB files now, option `--merge` is retired\n- Option `--suggest` now works at prompt as well\n- Auto-import issue when Firefox is not installed fixed\n\n-------------------------------------------------------------------------------\n\nBuku v3.3.1\n2017-09-11\n\nThis is for all purposes the same as v3.3. We had to re-upload a new version to\nPyPi and hence the new tag. Functionality remains the same.\n\nThe tagline is changed to - `Powerful command-line bookmark manager.`\n\n-------------------------------------------------------------------------------\n\nBuku v3.3\n2017-09-11\n\nWhat's in?\n- Auto-import (`--ai`) bookmarks from Firefox and Google Chrome\n- Support custom colors (`--colors`)\n- Search multiple tags (with exclusion)\n- Timestamp (YYYYMonDD) tag in auto-imported bookmarks\n- Enable browser output for text browsers\n- Generate documentation in RTD using Sphinx (http://buku.readthedocs.io)\n- Integrated flake8 and pylint in Travis CI\n- Integrated PackageCore to auto-generate packages in Travis CI\n\n-------------------------------------------------------------------------------\n\nBuku v3.2\n2017-08-03\n\nWhat's in?\n- Option `--suggest` to list and choose similar tags when adding a bookmark\n- Ask for a unique tag when importing bookmarks\n- Ignore non-generic URLs when importing browser exported bookmarks\n\n-------------------------------------------------------------------------------\n\nBuku v3.1\n2017-06-30\n\nWhat's in?\n- Handle negative indices (like tail) with option `-p`\n- Support browsing bookmarks from prompt (key `o`)\n- Add program search keywords to history\n- Support XDG_DATA_HOME and HOME as env vars on all platforms\n- Replace %USERPROFILE% with %APPDATA% as install location on Windows\n\n-------------------------------------------------------------------------------\n\nBuku v3.0\n2017-04-26\n\nWhat's in?\n- Edit bookmarks in EDITOR at prompt\n- Import folder names as tags from browser HTML (thanks @mohammadKhalifa)\n- Append, overwrite, delete tags at prompt using >>, >, << (familiar, eh? ;))\n- Negative indices with `--print` (like `tail`)\n- Update in EDITOR along with `--immutable`\n- Request HTTP HEAD for immutable records\n- Interface revamp (title on top in bold, colour changes...)\n- Per-level colourful logs in colour mode\n- Changes in program OPTIONS\n  - `-t` stands for tag search (earlier `--title`)\n  - `-r` stands for regex search (earlier `--replace`)\n- Lots of new automated test cases (thanks @rachmadaniHaryono)\n- REST APIs for server-side apps (thanks @kishore-narendran)\n- Document, notify behaviour when not invoked from tty (thanks @The-Compiler)\n- Fix Firefox tab-opening issues on Windows (thanks @dertuxmalwieder)\n\n-------------------------------------------------------------------------------\n\nBuku v2.9\n2017-02-20\n\nModifications\n- New option `--write` to compose and edit bookmarks in text editor\n- Support positional arguments as search keywords\n- New option `--oa` to search and open results directly in browser\n- Autodetect Markdown mode by file extension during export, import\n- Shortened options:\n    - `--nc` replaces `--nocolor`\n    - `--np` replaces `--noprompt`\n    - `-V` replaces `--upstream`\n- Option `--markdown` removed as the mode is autodetected now\n\n-------------------------------------------------------------------------------\n\nBuku v2.8\n2017-01-11\n\nModifications\n- Multithreaded full DB refresh with delayed writes\n- Customize number of threads for full DB refresh (default 4)\n- Support search and update search results in a go\n- Support shortened URL expansion\n- Support multiple bookmarks with `--open`\n- Support `--nocolor` (for scripting, Windows users)\n- Support https_proxy with `--upstream` and `--shorten`\n- Remove trailing `/` from search tokens (like Google search)\n- Support `--version` to show program version\n- Fixed #109: Missing + when shortening URL\n- Performance optimizations, few module dependency removals\n\n-------------------------------------------------------------------------------\n\nBuku v2.7\n2016-11-30\n\nModifications\n- Continuous search at (redesigned) prompt\n- urllib3 for all HTTP operations\n- Use HTTP HEAD method for pdf and txt mime types\n- Add user agent (Firefox 50 on Ubuntu)\n- Support URL shortening\n- List bookmarks by tag index in tag list\n- Show tag usage count in tag list\n- Store tags in lowercase (use undocumented option `--fixtags` to fix old tags)\n- Support environment variable *https_proxy*\n- Support option `--immutable` to pin titles\n- Keyword `immutable` to search (`-S`) pinned titles\n- Show index in JSON output\n- New key *q* to quit prompt\n- Support deflate compression\n- Add option `--tacit` to reduce verbosity of some operations\n- **Removed** option `--st`, only `--stag` to search tags\n- Support custom DB file location (for library, not exposed to user)\n\n-------------------------------------------------------------------------------\n\nBuku v2.6\n2016-11-04\n\nModifications\n- Support Markdown import/export\n- Support regex search\n- New option `--upstream` to check latest upstream version\n- Fix search and delete behaviour\n- Lot of code reformatting, performance improvements\n- Use delayed commit wherever possible (e.g. bulk deletion cases)\n- When a range is specified, consider 0 as ALL\n- Added option to control verbosity in some APIs\n- In-source documentation update\n\n-------------------------------------------------------------------------------\n\nBuku v2.5\n2016-10-20\n\nModifications\n- Export specific tags to HTML\n- Fixed obvious issues on Windows\n- Open a random bookmark with option --open\n- Support lists and ranges with --print\n- Show a bookmark on tag append\n- Show only title with --format=3\n- PEP8 compliance fixes\n- Buku GUI integration documented\n\n-------------------------------------------------------------------------------\n\nBuku v2.4\n2016-09-12\n\nModifications\n- Exact word match support using regex (**default**)\n- New option --deep to scan matching substrings\n- Support DB index lists and ranges in update operation\n- Open a list or range of search results in browser\n- Open all search results in browser\n- A more concise prompt\n- PEP8 compliance (almost)\n- Tons of new test cases added (thanks @wheresmyjetpack)\n\n-------------------------------------------------------------------------------\n\nBuku v2.3\n2016-07-14\n\nModifications\n- Delete a range or a list of indices\n- Delete tag from tagset by bookmark index\n- Delete results of a particular search\n- Linked to rofi front-end script project for Buku\n- Use the logging framework for debug info instead of print\n- Fixed an issue with gzip stream decoding\n- Using only relative path to fetch resource on server\n- Fixed auto-completion errors with Zsh\n- A lot of code cleanup and globals removed, additional test cases\n\n-------------------------------------------------------------------------------\n\nBuku v2.2\n2016-06-12\n\nModifications\n- Export bookmarks to Firefox bookmarks formatted HTML\n- Merge Buku database\n- .deb package for Debian and Ubuntu family\n- Switch from PyCrypto to cryptography (thanks @asergi)\n- Append tags support\n- Filter tags for duplicates and sort alphabetically\n- Travis CI integration, more test cases (thanks @poikjhn)\n- Show DB index in bold in search results\n- Several performance optimizations\n\n-------------------------------------------------------------------------------\n\nBuku v2.1\n2016-05-28\n\nModifications\n- Import bookmarks from Firefox, Google Chrome or IE HTML bookmark exports\n- Support comments on bookmarks\n- Prettier output using symbols (`>` title, `+` comments, `#` tags)\n- New option (`--st`, `--stag`) to search by tag\n- New option (`--noprompt`) for noninteractive mode\n- New options (`--url` and `--tag`)\n- `--update` now handles each option (url, tag, title, comment) independently\n- Several messages removed or moved to debug\n\n-------------------------------------------------------------------------------\n\nBuku v2.0\n2016-05-15\n\nModifications\nTo begin with, 2.0 is a significant release with respect to options. `Buku` now has fewer options with more (and merged) functionality. Please go through the program help at least once to understand the changes.\n\n- Replace getopt with argparse for parsing arguments\n- Long options for each short option\n- Options changed\n    - insert: removed as automatic DB compaction serves the purpose (previously `-i`)\n    - iterations: removed as optional argument to `-l` and `-k` (previously `-t`)\n    - title: `-t` is now the short option to set title manually (previously `-m`)\n    - Special search keywords for ALL search (`-S`):\n        - tags: show all tags (previously `-g`)\n        - blank: show bookmarks with empty tags (previously `-e`)\n    - lock/unlock: now accepts number of hash iterations to generate key\n    - format: print formatting option changed to `-f` (previously `-x`)\n    - help: option added to show program help\n- Following options apply to ALL bookmarks without arguments\n    - `-u`, `--update`\n    - `-d`, `--delete`\n    - `-p`, `--print`\n- Shell-completion scripts for Bash, Fish and Zsh\n- Warn if URL is not HTTP(S)\n- More comprehensive help\n- Fix a bug with deletion when only one entry in DB\n- Some import dependencies removed or late loaded (if optional)\n- Handle exception if DB file is encrypted or invalid\n\n-------------------------------------------------------------------------------\n\nBuku v1.9\n2016-04-23\n\nModifications\n- **New location for database file** (refer to README or man page). The old database file, if exists, is migrated automatically.\n- **Removed options**\n    - `-P`: (print all) is now `-p 0`\n    - `-D`: (delete all) is now `-d 0`\n    - `-R`: (update all) is now `-u 0`\n    - `-w`: title web fetch is now the default behaviour, override with `-m title` option\n- **Change in search behaviour**\n    - `-s`: search bookmarks for ANY keyword in URL, title or tags\n    - `-S`: search bookmarks for ALL keywords in URL, title or tags\n- Update only title of a bookmark (`-u N`)\n- Set empty title (`-m none`)\n- Support HTTP(S) gzip compression\n- Optional JSON output for `-p` and `-s` options (thanks @CaptainQuirk)\n- Reformatted help and man page with general options on top\n- Optimize add and insert: ensure URL is not in DB already\n- Handle URLs passed with %xx escape\n- Retry with truncated resource path on HTTP error 500\n- Several code optimizations\n- Catchier errors and warnings\n- Version added to debug logs\n\n-------------------------------------------------------------------------------\n\nBuku v1.8\n2016-03-26\n\nModifications\n- Auto compact DB on single record removal\n- Handle piped input\n- Better tag management\n    - Tag modify or delete support\n    - Show unique tags alphabetically\n- Full DB refresh\n    - Fix stuff broken earlier\n    - Optimize to update titles only\n    - Update titles only if non-empty to preserve earlier data\n- Redirection\n    - Handle multiple redirections\n    - Detect redirection loop and break\n    - Show redirected link in bold\n- List all bookmarks with no title or tags (for manual bookkeeping)\n- Confirm full DB removal\n- Better comma (`,`) separator handling for tags\n- Help\n    - Place regular options before power options in program help\n    - Help added in man page for quick reference\n    - Additional examples for new features\n- Errors & warnings\n    - Error out if both encrypted and flat DB files exist\n    - Catchier error and warning messages\n\n-------------------------------------------------------------------------------\n\nBuku v1.7\n2016-03-15\n\nModifications\n- Add title manually using option `-m`\n- Unquote redirected URL\n- Quit on `Ctrl-d` at prompt\n- More dynamic shebang for python3\n\n-------------------------------------------------------------------------------\n\nBuku v1.6\n2016-01-22\n\nModifications\n- Stronger encryption: 256-bit salt, multi-hash key.\n- Allow user to specify number of iterations to generate key (check option `-t`).\n\n-------------------------------------------------------------------------------\n\nBuku v1.5\n2015-12-20\n\nModifications\n- Project name changed to `Buku` to avoid any copyright issues. This also means old users have to move the database file. Run:\n<pre>$ mkdir ~/.cache/buku/\n$ mv ~/.cache/markit/bookmarks.db ~/.cache/buku/bookmarks.db\n$ rm -rf ~/.cache/markit/bookmarks.db</pre>\n- Manual AES-256 encryption and decryption support (password protection) implemented. This adds dependency on PyCrypto module. Installation instructions updated in README.\n- Some typos fixed (thanks @GuilhermeHideki)\n\n-------------------------------------------------------------------------------\n\nMarkIt v1.4\n2015-11-13\n\nModifications\n- Refresh full bookmark database. Fetch titles from the web, retain tags.\n- Notify empty titles in red during online add or update.\n\n-------------------------------------------------------------------------------\n\nMarkIt v1.2\n2015-11-11\n\nModifications\n- Introduced `-S` search option to match ALL keywords in URL or title\n- Introduced `-x` option to show unformatted selective output (for creating batch scripts)\n- Added examples on batch add and update (refresh) scripts\n- Handle multiple title tags in page\n- Handle title data within another tag (e.g. head)\n- Show DB index in search results, removal and update confirmation message\n\n-------------------------------------------------------------------------------\n\nMarkIt v1.1\n2015-11-10\n\nModifications\n- Replace Unicode chars in title data before UTF-8 decoding (for parser to succeed).\n\n-------------------------------------------------------------------------------\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM python:3.14.0-alpine\n\nLABEL org.opencontainers.image.authors=\"shenoy.ameya@gmail.com\"\n\nENV BUKUSERVER_PORT=5001\n\nCOPY . /buku\n\nARG CRYPTOGRAPHY_DONT_BUILD_RUST=1\n\nRUN set -ex \\\n  && apk add --no-cache --virtual .build-deps \\\n    gcc \\\n    openssl-dev \\\n    musl-dev \\\n    libffi-dev \\\n  && pip install -U --no-cache-dir \\\n    pip \\\n    gunicorn \\\n    /buku[server] \\\n  && apk del .build-deps \\\n  && echo \"import sys;  bind = '0.0.0.0:${BUKUSERVER_PORT}'\" > 'gunicorn.conf.py' \\\n  && rm -rf /buku\n\nHEALTHCHECK --interval=1m --timeout=10s \\\n  CMD nc -z 127.0.0.1 ${BUKUSERVER_PORT} || exit 1\n\nENTRYPOINT [\"gunicorn\", \"bukuserver.server:create_app()\"]\nEXPOSE ${BUKUSERVER_PORT}\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    {one line to give the program's name and a brief idea of what it does.}\n    Copyright (C) {year}  {name of author}\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    {project}  Copyright (C) {year}  {fullname}\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<http://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<http://www.gnu.org/philosophy/why-not-lgpl.html>.\n\n"
  },
  {
    "path": "MANIFEST.in",
    "content": "include CHANGELOG LICENSE README.md buku.1 requirements.txt\ninclude tests/test_bukuDb/Bookmarks\nrecursive-include tests *.py\nrecursive-include tests/test_bukuDb *.yaml\nrecursive-include auto-completion *\nrecursive-include bukuserver/apidocs *\nrecursive-include bukuserver/templates *\nrecursive-include bukuserver/static *\n"
  },
  {
    "path": "Makefile",
    "content": "PREFIX ?= /usr/local\nBINDIR ?= $(DESTDIR)$(PREFIX)/bin\nMANDIR ?= $(DESTDIR)$(PREFIX)/share/man/man1\nDOCDIR ?= $(DESTDIR)$(PREFIX)/share/doc/buku\n\n.PHONY: all install uninstall\n\nall:\n\ninstall:\n\tinstall -m755 -d $(BINDIR)\n\tinstall -m755 -d $(MANDIR)\n\tinstall -m755 -d $(DOCDIR)\n\tgzip -c buku.1 > buku.1.gz\n\tinstall -m755 buku.py $(BINDIR)/buku\n\tinstall -m644 buku.1.gz $(MANDIR)\n\tinstall -m644 README.md $(DOCDIR)\n\trm -f buku.1.gz\n\nuninstall:\n\trm -f $(BINDIR)/buku\n\trm -f $(MANDIR)/buku.1.gz\n\trm -rf $(DOCDIR)\n"
  },
  {
    "path": "README.md",
    "content": "<h1 align=\"center\">buku</h1>\n\n<p align=\"center\">\n<a href=\"https://github.com/jarun/buku/releases/latest\"><img src=\"https://img.shields.io/github/release/jarun/buku.svg?maxAge=600\" alt=\"Latest release\" /></a>\n<a href=\"https://repology.org/project/buku/versions\"><img src=\"https://repology.org/badge/tiny-repos/buku.svg?header=repos\" alt=\"Availability\"></a>\n<a href=\"https://pypi.org/project/buku/\"><img src=\"https://img.shields.io/pypi/v/buku.svg?maxAge=600\" alt=\"PyPI\" /></a>\n<a href=\"https://circleci.com/gh/jarun/workflows/buku\"><img src=\"https://img.shields.io/circleci/project/github/jarun/buku.svg\" alt=\"Build Status\" /></a>\n<a href=\"https://buku.readthedocs.io/en/latest/?badge=latest\"><img src=\"https://readthedocs.org/projects/buku/badge/?version=latest\" alt=\"Docs Status\" /></a>\n<a href=\"https://en.wikipedia.org/wiki/Privacy-invasive_software\"><img src=\"https://img.shields.io/badge/privacy-✓-crimson\" alt=\"Privacy Awareness\" /></a>\n<a href=\"https://github.com/jarun/buku/blob/master/LICENSE\"><img src=\"https://img.shields.io/badge/license-GPLv3-yellowgreen.svg?maxAge=2592000\" alt=\"License\" /></a>\n</p>\n\n<p align=\"center\">\n<a href=\"https://github.com/user-attachments/assets/a65950a9-3c82-4c6d-9184-dead83f9c759\"><img src=\"https://github.com/user-attachments/assets/6688d1eb-e268-4b00-8e8f-044803c3040f\" alt=\"buku in action!\" width=\"734\"/></a>\n</p>\n\n<p align=\"center\"><i>buku in action!</i></p>\n\n### Introduction\n\n`buku` is a powerful bookmark manager and a personal textual mini-web.\n\nFor those who prefer the GUI, `bukuserver` exposes a browsable front-end on a local web host server. See [bukuserver page](https://github.com/jarun/buku/tree/master/bukuserver#readme) for config and screenshots.\n\nWhen I started writing it, I couldn't find a flexible command-line solution with a private, portable, merge-able database along with seamless GUI integration. Hence, `buku`.\n\n`buku` can import bookmarks from browser(s) or fetch the title, tags and description of a URL from the web. Use your favourite editor to add, compose and update bookmarks. Search bookmarks instantly with multiple search options, including regex and a deep scan mode (handy with URLs).\n\nIt can look up broken links on the Wayback Machine. There's an Easter Egg to revisit random bookmarks.\n\nThere's no tracking, hidden history, obsolete records, usage analytics or homing.\n\nTo get started right away, jump to the [Quickstart](#quickstart) section. `buku` has one of the best documentation around. The man page comes with examples. For internal details, please refer to the [operational notes](https://github.com/jarun/buku/wiki/Operational-notes).\n\n`buku` is a library too! There are several related projects, including a browser plug-in.\n\n### Table of Contents\n\n- [Features](#features)\n- [Installation](#installation)\n  - [Dependencies](#dependencies)\n  - [From a package manager](#from-a-package-manager)\n  - [Release packages](#release-packages)\n  - [From source](#from-source)\n  - [Running standalone](#running-standalone)\n- [Shell completion](#shell-completion)\n- [Usage](#usage)\n  - [Command-line options](#command-line-options)\n  - [Colors](#colors)\n- [Quickstart](#quickstart)\n- [Examples](#examples)\n- [Automation](#automation)\n- [Troubleshooting](#troubleshooting)\n  - [Editor integration](#editor-integration)\n- [Collaborators](#collaborators)\n- [Contributions](#contributions)\n- [Related projects](#related-projects)\n- [In the Press](#in-the-press)\n\n### Features\n\n- Store bookmarks with auto-fetched title, tags and description\n- Auto-import from Firefox, Google Chrome, Chromium, Vivaldi, Brave, and MS Edge\n- Open bookmarks and search results in browser\n- Browse cached page from the Wayback Machine\n- Text editor integration\n- Lightweight, clean interface, custom colors\n- Powerful search options (regex, substring...)\n- Continuous search with on the fly mode switch\n- Portable, merge-able database to sync between systems\n- Import/export bookmarks from/to HTML, XBEL, Markdown, RSS/Atom or Orgfile\n- Smart tag management using redirection (>>, >, <<)\n- Multi-threaded full DB refresh\n- Manual encryption support\n- Shell completion scripts, man page with handy examples\n- Privacy-aware (no unconfirmed user data collection)\n- Can be used as a Python library ([_API documentation_](https://buku.readthedocs.io/en/latest/?badge=latest))\n- Has a compation Web-application ([Bukuserver](https://github.com/jarun/buku/wiki/Bukuserver-%28WebUI%29)) with an HTTP-based API (for personal use only)\n\n### Installation\n\n#### Dependencies\n\n| Feature | Dependency |\n| --- | --- |\n| Lang, SQLite | Python 3.10+ |\n| HTTPS | certifi, urllib3 |\n| Encryption | cryptography |\n| HTML | beautifulsoup4, html5lib |\n\nTo copy URL to clipboard `buku` uses `xsel` (or `xclip`) on Linux, `pbcopy` (default installed) on OS X, `clip` (default installed) on Windows, `termux-clipboard` on Termux (terminal emulation for Android), `wl-copy` on Wayland. If X11 is missing, GNU Screen or tmux copy-paste buffers are recognized.\n\n#### From a package manager\n\nTo install buku with all its dependencies from PyPI, run:\n\n    # pip3 install buku\n\nYou can also install `buku` from your package manager. If the version available is dated try an alternative installation method.\n\n<details><summary>Packaging status (expand)</summary>\n<p>\n<br>\n<a href=\"https://repology.org/project/buku/versions\"><img src=\"https://repology.org/badge/vertical-allrepos/buku.svg\" alt=\"Packaging status\"></a>\n</p>\nUnlisted packagers:\n<p>\n<br>\n● <a href=\"https://pypi.org/project/buku/\">PyPI</a> (<code>pip3 install buku</code>)<br>\n● Termux (<code>pip3 install buku</code>)<br>\n</p>\n</details>\n\n#### Release packages\n\nAuto-generated packages (with only the cli component) for Arch Linux, CentOS, Debian, Fedora, openSUSE Leap and Ubuntu are available with the [latest stable release](https://github.com/jarun/buku/releases/latest).\n\nNOTE: CentOS may not have the python3-beautifulsoup4 package in the repos. Install it using pip3.\n\n#### From source\n\nIf you have git installed, clone this repository. Otherwise download the [latest stable release](https://github.com/jarun/buku/releases/latest) or [development version](https://github.com/jarun/buku/archive/master.zip) (*risky*).\n\nInstall the dependencies. For example, on Ubuntu:\n\n    $ apt-get install ca-certificates python3-urllib3 python3-cryptography python3-bs4\n\nInstall the cli component to default location (`/usr/local`):\n\n    $ sudo make install\n\nTo remove, run:\n\n    $ sudo make uninstall\n\n`PREFIX` is supported, in case you want to install to a different location.\n\n#### Running standalone\n\n`buku` is a standalone utility. From the containing directory, run:\n\n    $ chmod +x buku.py\n    $ ./buku.py\n\n### Shell completion\n\nShell completion scripts for Bash, Fish and Zsh can be found in respective subdirectories of [auto-completion/](https://github.com/jarun/buku/blob/master/auto-completion). Please refer to your shell's manual for installation instructions.\n\n### Usage\n\n#### Command-line options\n\n```\nusage: buku [OPTIONS] [KEYWORD [KEYWORD ...]]\n\nBookmark manager like a text-based mini-web.\n\nPOSITIONAL ARGUMENTS:\n      KEYWORD              search keywords\n\nGENERAL OPTIONS:\n      -a, --add URL [+|-] [tag, ...]\n                           bookmark URL with comma-separated tags\n                           (prepend tags with '+' or '-' to use fetched tags)\n      -u, --update [...]   update fields of an existing bookmark\n                           accepts indices and ranges\n                           refresh title and desc if no edit options\n                           if no arguments:\n                           - update results when used with search\n                           - otherwise refresh all titles and desc\n      -w, --write [editor|index]\n                           edit and add a new bookmark in editor\n                           else, edit bookmark at index in EDITOR\n                           edit last bookmark, if index=-1\n                           if no args, edit new bookmark in EDITOR\n      -d, --delete [...]   remove bookmarks from DB\n                           accepts indices or a single range\n                           if no arguments:\n                           - delete results when used with search\n                           - otherwise delete all bookmarks\n      --retain-order       prevents reordering after deleting a bookmark\n      -h, --help           show this information and exit\n      -v, --version        show the program version and exit\n\nEDIT OPTIONS:\n      --url keyword        bookmark link\n      --tag [+|-] [...]    comma-separated tags\n                           clear bookmark tagset, if no arguments\n                           '+' appends to, '-' removes from tagset\n      --title [...]        bookmark title; if no arguments:\n                           -a: do not set title, -u: clear title\n      -c, --comment [...]  notes or description of the bookmark\n                           clears description, if no arguments\n      --immutable N        disable web-fetch during auto-refresh\n                           N=0: mutable (default), N=1: immutable\n      --swap N M           swap two records at specified indices\n\nSEARCH OPTIONS:\n      -s, --sany [...]     find records with ANY matching keyword\n                           this is the default search option\n      -S, --sall [...]     find records matching ALL the keywords\n                           special keywords -\n                           \"blank\": entries with empty title/tag\n                           \"immutable\": entries with locked title\n      --deep               match substrings ('pen' matches 'opens')\n      --markers            search for keywords in specific fields\n                           based on (optional) prefix markers:\n                           '.' - title, '>' - description, ':' - URL,\n                           '#' - tags (comma-separated, PARTIAL matches)\n                           '#,' - tags (comma-separated, EXACT matches)\n                           '*' - any field (same as no prefix)\n      -r, --sreg expr      run a regex search\n      -t, --stag [tag [,|+] ...] [- tag, ...]\n                           search bookmarks by tags\n                           use ',' to find entries matching ANY tag\n                           use '+' to find entries matching ALL tags\n                           excludes entries with tags after ' - '\n                           list all tags, if no search keywords\n      -x, --exclude [...]  omit records matching specified keywords\n      --random [N]         output random bookmarks out of the selection (default 1)\n      --order fields [...] comma-separated list of fields to order the output by\n                           (prepend with '+'/'-' to choose sort direction)\n\nENCRYPTION OPTIONS:\n      -l, --lock [N]       encrypt DB in N (default 8) # iterations\n      -k, --unlock [N]     decrypt DB in N (default 8) # iterations\n\nPOWER TOYS:\n      --ai                 auto-import bookmarks from web browsers\n                           Firefox, Chrome, Chromium, Vivaldi, Brave, Edge\n                           (Firefox profile can be specified using\n                           environment variable FIREFOX_PROFILE)\n      -e, --export file    export bookmarks to Firefox format HTML\n                           export XBEL, if file ends with '.xbel'\n                           export Markdown, if file ends with '.md'\n                           format: [title](url) <!-- TAGS -->\n                           export Orgfile, if file ends with '.org'\n                           format: *[[url][title]] :tags:\n                           export rss feed if file ends with '.rss'/'.atom'\n                           export buku DB, if file ends with '.db'\n                           combines with search results, if opted\n      -i, --import file    import bookmarks from file\n                           supports .html .xbel .json .md .org .rss .atom .db\n                           (.json = Firefox backup; .db = another Buku DB)\n      -p, --print [...]    show record details by indices, ranges\n                           print all bookmarks, if no arguments\n                           -n shows the last n results (like tail)\n      -f, --format N       limit fields in -p or JSON search output\n                           N=1: URL; N=2: URL, tag; N=3: title;\n                           N=4: URL, title, tag; N=5: title, tag;\n                           N0 (10, 20, 30, 40, 50) omits DB index\n      -j, --json [file]    JSON formatted output for -p and search.\n                           prints to stdout if argument missing.\n                           otherwise writes to given file\n      --colors COLORS      set output colors in five-letter string\n      --nc                 disable color output\n      -n, --count N        show N results per page (default 10)\n      --np                 do not show the subprompt, run and exit\n      -o, --open [...]     browse bookmarks by indices and ranges\n                           open a random bookmark, if no arguments\n      --oa                 browse all search results immediately\n      --default-scheme S   if scheme is missing from uri, assume S\n                           when opening in browser (default http)\n      --replace old new    replace old tag with new tag everywhere\n                           delete old tag, if new tag not specified\n      --url-redirect       when fetching an URL, use the resulting\n                           URL from following *permanent* redirects\n                           (when combined with --export, the old URL\n                           is included as additional metadata)\n      --tag-redirect [tag] when fetching an URL that causes permanent\n                           redirect, add a tag in specified pattern\n                           (using 'http:{}' if not specified)\n      --tag-error [tag]    when fetching an URL that causes an HTTP\n                           error, add a tag in specified pattern\n                           (using 'http:{}' if not specified)\n      --del-error [...]    when fetching an URL causes any (given)\n                           HTTP error, delete/do not add it\n      --export-on [...]    export records affected by the above\n                           options, including removed info\n                           (requires --update and --export; specific\n                           HTTP response filter can be provided)\n      --reorder order...   update DB indices to match specified order\n      --cached index|URL   browse a cached page from Wayback Machine\n      --offline            add a bookmark without connecting to web\n      --suggest            show similar tags when adding bookmarks\n      --tacit              reduce verbosity, skip some confirmations\n      --nostdin            do not wait for input (must be first arg)\n      --threads N          max network connections in full refresh\n                           default N=4, min N=1, max N=10\n      -V                   check latest upstream version available\n      -g, --debug          show debug information and verbose logs\n\nSYMBOLS:\n      >                    url\n      +                    comment\n      #                    tags\n\nPROMPT KEYS:\n    1-N                    browse search result indices and/or ranges\n    R [N]                  print out N random search results\n                           (or random bookmarks if negative or N/A)\n    ^ id1 id2              swap two records at specified indices\n    O [id|range [...]]     open search results/indices in GUI browser\n                           toggle try GUI browser if no arguments\n    a                      open all results in browser\n    s keyword [...]        search for records with ANY keyword\n    S keyword [...]        search for records with ALL keywords\n    d                      match substrings ('pen' matches 'opened')\n    m                      search with markers - search string is split\n                           into keywords by prefix markers, which determine\n                           what field the keywords is searched in:\n                           '.', '>' or ':' - title, description or URL\n                           '#'/'#,' - tags (comma-separated, partial/full match)\n                           '*' - all fields (can be omitted in the 1st keyword)\n                           note: tag marker is not affected by 'd' (deep search)\n    v fields               change sorting order (default is '+index')\n                           multiple comma/space separated fields can be specified\n    v! fields              update indices in DB to match specified order\n    r expression           run a regex search\n    t [tag, ...]           search by tags; show taglist, if no args\n    g taglist id|range [...] [>>|>|<<] [record id|range ...]\n                           append, set, remove (all or specific) tags\n                           search by taglist id(s) if records are omitted\n    n                      show next page of search results\n    N                      show previous page of search results\n    o id|range [...]       browse bookmarks by indices and/or ranges\n    p id|range [...]       print bookmarks by indices and/or ranges\n    w [editor|id]          edit and add or update a bookmark\n    c id                   copy URL at search result index to clipboard\n    DB [name]              check existing DB list or switch to another DB\n                           (use full/dir path to switch folders)\n                           '~.' can be used as shortcut for default DB\n    ?                      show this help\n    q, ^D, double Enter    exit buku\n```\n\n#### Colors\n\n`buku` supports custom colors. Visit the wiki page on how to [customize colors](https://github.com/jarun/buku/wiki/Customize-colors) for more details.\n\n### Quickstart\n\n1. Export `VISUAL` or `EDITOR` to point to your favourite editor. Note that `VISUAL` takes precedence over `EDITOR`.\n2. Create a sweeter shortcut with some convenience.\n\n       alias b='buku --suggest'\n3. Auto-import bookmarks from your browser(s). Please quit the relevant browsers beforehand to ensure the databases are not locked.\n\n       b --ai\n4. Manually add a bookmark (for hands-on).\n\n       b -w\n5. List your bookmarks with DB index.\n\n       b -p\n6. For GUI and browser integration (or to sync bookmarks with your favourite bookmark management service) refer to the wiki page on [System integration](https://github.com/jarun/buku/wiki/System-integration).\n7. Quick (bash/zsh) commands to fuzzy search with fzf and open the selection in Firefox:\n\n       firefox $(buku -p -f 10 | fzf)\n       firefox $(buku -p -f 40 | fzf | cut -f1)\n\n   POSIX script to show a preview of the bookmark as well:\n\n   ```sh\n   #!/usr/bin/env sh\n\n   url=$(buku -p -f4 | fzf -m --reverse --preview \"buku -p {1}\" --preview-window=wrap | cut -f2)\n\n   if [ -n \"$url\" ]; then\n       echo \"$url\" | xargs firefox\n   fi\n   ```\n\n### Examples\n\n1. **Edit and add** a bookmark from editor:\n\n       $ buku -w\n       $ buku -w 'gedit -w'\n       $ buku -w 'macvim -f' -a https://ddg.gg search engine, privacy\n    The first command picks editor from the environment variable `EDITOR`. The second command opens gedit in blocking mode. The third command opens macvim with option -f and the URL and tags populated in template.\n2. **Add** a simple bookmark:\n\n       $ buku --nostdin -a https://github.com/\n       2648. GitHub: Let’s build from here · GitHub\n       > https://github.com/\n       + GitHub is where over 94 million developers shape the future of software, together. Contribute to the open source community, manage your Git repositories, review code like a pro, track bugs\n        and features, power your CI/CD and DevOps workflows, and secure code before you commit it.\n\n       $ buku --nostdin -a https://github.com/\n       [ERROR] URL [https://github.com/] already exists at index 2648\n\n      `>`: URL, `+`: comment, `#`: tags\n\n      Title, description and tags will be fetched from site. Buku only stores unique URLs and will raise error if the URL already present in the database:\n3. **Add** a bookmark with **tags** `search engine` and `privacy`, **comment** `Search engine with perks`, **fetch page title** from the web:\n\n       $ buku -a https://ddg.gg search engine, privacy -c Search engine with perks\n       336. DuckDuckGo\n       > https://ddg.gg\n       + Alternative search engine with perks\n       # privacy,search engine\n    where, `>`: URL, `+`: comment, `#`: tags\n4. **Add** a bookmark with tags `search engine` & `privacy` and **immutable custom title** `DDG`:\n\n       $ buku -a https://ddg.gg search engine, privacy --title 'DDG' --immutable 1\n       336. DDG (L)\n       > https://ddg.gg\n       # privacy,search engine\n    Note that URL must precede tags.\n5. **Add** a bookmark **without a title** (works for update too):\n\n       $ buku -a https://ddg.gg search engine, privacy --title\n6. **Edit and update** a bookmark from editor:\n\n       $ buku -w 15012014\n    This will open the existing bookmark's details in the editor for modifications. Environment variable `EDITOR` must be set.\n7. **Update** existing bookmark at index 15012014 with new URL, tags and comments, fetch title from the web:\n\n       $ buku -u 15012014 --url http://ddg.gg/ --tag web search, utilities -c Private search engine\n8. **Fetch and update only title** for bookmark at 15012014:\n\n       $ buku -u 15012014\n9. **Update only comment** for bookmark at 15012014:\n\n       $ buku -u 15012014 -c this is a new comment\n    Applies to --url, --title and --tag too.\n10. **Export** bookmarks tagged `tag 1` or `tag 2` to HTML, XBEL, Markdown, Orgfile or a new database:\n\n       $ buku -e bookmarks.html --stag tag 1, tag 2\n       $ buku -e bookmarks.xbel --stag tag 1, tag 2\n       $ buku -e bookmarks.md --stag tag 1, tag 2\n       $ buku -e bookmarks.org --stag tag 1, tag 2\n       $ buku -e bookmarks.db --stag tag 1, tag 2\n    All bookmarks are exported if search is not opted.\n11. **Import** bookmarks from HTML, XBEL, Markdown or Orgfile:\n\n        $ buku -i bookmarks.html\n        $ buku -i bookmarks.xbel\n        $ buku -i bookmarks.md\n        $ buku -i bookmarks.org\n        $ buku -i bookmarks.db\n12. **Delete only comment** for bookmark at 15012014:\n\n        $ buku -u 15012014 -c\n    Applies to --title and --tag too. URL cannot be deleted without deleting the bookmark.\n13. **Update** or refresh **full DB** with page titles from the web:\n\n        $ buku -u\n        $ buku -u --tacit (show only failures and exceptions)\n    This operation can update the title or description fields of non-immutable bookmarks by parsing the fetched page. Fields are updated only if the fetched fields are non-empty. Tags remain untouched.\n14. **Delete** bookmark at index 15012014:\n\n        $ buku -d 15012014\n        Index 15012020 moved to 15012014\n    The last index is moved to the deleted index to keep the DB compact. Add `--tacit` to delete without confirmation.\n15. **Delete all** bookmarks:\n\n        $ buku -d\n16. **Delete** a **range or list** of bookmarks:\n\n        $ buku -d 100-200\n        $ buku -d 100 15 200\n17. **Search** bookmarks for **ANY** of the keywords `kernel` and `debugging` in URL, title or tags:\n\n        $ buku kernel debugging\n        $ buku -s kernel debugging\n18. **Search** bookmarks with **ALL** the keywords `kernel` and `debugging` in URL, title or tags:\n\n        $ buku -S kernel debugging\n19. **Search** bookmarks **tagged** `general kernel concepts`:\n\n        $ buku --stag general kernel concepts\n20. **Search** for bookmarks matching **ANY** of the tags `kernel`, `debugging`, `general kernel concepts`:\n\n        $ buku --stag kernel, debugging, general kernel concepts\n21. **Search** for bookmarks matching **ALL** of the tags `kernel`, `debugging`, `general kernel concepts`:\n\n        $ buku --stag kernel + debugging + general kernel concepts\n22. **Search** for bookmarks matching any of the keywords `hello` or `world`, excluding the keywords `real` and `life`, matching both the tags `kernel` and `debugging`, but **excluding** the tags `general kernel concepts` and `books`:\n\n        $ buku hello world --exclude real life --stag 'kernel + debugging - general kernel concepts, books'\n23. **Search** for bookmarks with different tokens for each field, and print them out sorted by the tags (ascending) and URL (descending)\n\n        $ buku --order +tags,-url --markers --sall 'global substring' '.title substring' ':url substring' :https '> description substring' '#partial,tags:' '#,exact,tags' '*another global substring'\n24. List **all unique tags** alphabetically:\n\n        $ buku --stag\n25. Run a **search and update** the results:\n\n        $ buku -s kernel debugging -u --tag + linux kernel\n26. Run a **search and delete** the results:\n\n        $ buku -s kernel debugging -d\n27. **Encrypt or decrypt** DB with **custom number of iterations** (15) to generate key:\n\n        $ buku -l 15\n        $ buku -k 15\n    The same number of iterations must be specified for one lock & unlock instance. Default is 8, if omitted.\n28. **Show details** of bookmarks at index 15012014 and ranges 20-30, 40-50:\n\n        $ buku -p 20-30 15012014 40-50\n29. Show details of the **last 10 bookmarks**:\n\n        $ buku -p -10\n30. **Show all** bookmarks with real index from database:\n\n        $ buku -p\n        $ buku -p | more\n31. **Replace tag** 'old tag' with 'new tag':\n\n        $ buku --replace 'old tag' 'new tag'\n32. **Delete tag** 'old tag' from DB:\n\n        $ buku --replace 'old tag'\n33. **Append (or delete) tags** 'tag 1', 'tag 2' to (or from) existing tags of bookmark at index 15012014:\n\n        $ buku -u 15012014 --tag + tag 1, tag 2\n        $ buku -u 15012014 --tag - tag 1, tag 2\n34. **Open URL** at index 15012014 in browser:\n\n        $ buku -o 15012014\n35. List bookmarks with **no title or tags** for bookkeeping:\n\n        $ buku -S blank\n36. List bookmarks with **immutable title**:\n\n        $ buku -S immutable\n37. **Append, remove tags at prompt** (taglist index to the left, bookmark index to the right):\n\n        // append tags at taglist indices 4 and 6-9 to existing tags in bookmarks at indices 5 and 2-3\n        buku (? for help) g 4 9-6 >> 5 3-2\n        // set tags at taglist indices 4 and 6-9 as tags in bookmarks at indices 5 and 2-3\n        buku (? for help) g 4 9-6 > 5 3-2\n        // remove all tags from bookmarks at indices 5 and 2-3\n        buku (? for help) g > 5 3-2\n        // remove tags at taglist indices 4 and 6-9 from tags in bookmarks at indices 5 and 2-3\n        buku (? for help) g 4 9-6 << 5 3-2\n38. List bookmarks with **colored output**:\n\n        $ buku --colors oKlxm -p\n39. Add a bookmark after following all permanent redirects, but only if the server doesn't respond with an error (and there's no network failure)\n\n        $ buku --add http://wikipedia.net --url-redirect --del-error\n        2. Wikipedia\n           > https://www.wikipedia.org/\n           + Wikipedia is a free online encyclopedia, created and edited by volunteers around the world and hosted by the Wikimedia Foundation.\n40. Add a bookmark with tag `http redirect` if the server responds with a permanent redirect, or tag shaped like `http 404` on an error response:\n\n        $ buku --add http://wikipedia.net/notfound --tag-redirect 'http redirect' --tag-error 'http {}'\n        [ERROR] [404] Not Found\n        3. Not Found\n           > http://wikipedia.net/notfound\n           # http 404,http redirect\n41. Update all bookmarks matching the search by updating the URL if the server responds with a permanent redirect, deleting the bookmark if the server responds with HTTP error 400, 401, 402, 403, 404 or 500, or adding a tag shaped like `http:{}` in case of any other HTTP error; then export those affected by such changes into an HTML file, marking deleted records as well as old URLs for those replaced by redirect.\n\n        $ buku -S ://wikipedia.net -u --url-redirect --tag-error --del-error 400-404,500 --export-on --export backup.html\n\n42. Print out a single **random** bookmark:\n\n        $ buku --random --print\n\n43. Print out 3 **random** bookmarks **ordered** by netloc (reversed), title and url:\n\n        $ buku --random 3 --order ,-netloc,title,+url --print\n\n44. Print out a single **random** bookmark matching **search** criteria, and **export** into a Markdown file (in DB order):\n\n        $ buku --random -S kernel debugging --export random.md\n\n45. Swap positions of records #4 and #5:\n\n        $ buku --swap 4 5\n\n46. Update indices in all bookmarks to match specified order:\n\n        $ buku --reorder ,-netloc,title,+url\n\n47. More **help**:\n\n        $ buku -h\n        $ man buku\n\n### Automation\n\nInteractive workflows can be automated using expect. Issue [#368](https://github.com/jarun/buku/issues/368) has a working example on automating auto-import.\n\n### Troubleshooting\n\n#### Editor integration\n\nYou may encounter issues with GUI editors which maintain only one instance by default and return immediately from other instances. Use the appropriate editor option to block the caller when a new document is opened. See issue [#210](https://github.com/jarun/buku/issues/210) for gedit.\n\n### Collaborators\n\n- [Arun Prakash Jana](https://github.com/jarun)\n- [Alexey Gulenko](https://github.com/LeXofLeviafan)\n- [Rachmadani Haryono](https://github.com/rachmadaniHaryono)\n- [Johnathan Jenkins](https://github.com/shaggytwodope)\n- [SZ Lin](https://github.com/szlin)\n\nCopyright © 2015-2026 [Arun Prakash Jana](mailto:engineerarun@gmail.com)\n<br>\n<p><a href=\"https://gitter.im/jarun/buku\"><img src=\"https://img.shields.io/gitter/room/jarun/buku.svg?maxAge=2592000\" alt=\"gitter chat\" /></a></p>\n\n### Contributions\n\nMissing a feature? There's a rolling [ToDo List](https://github.com/jarun/buku/issues/484) with identified tasks. Contributions are welcome! Please follow the [PR guidelines](https://github.com/jarun/buku/wiki/PR-guidelines).\n\nSee also our documentation here <a href=\"http://buku.readthedocs.io/en/stable/?badge=stable\"><img src=\"https://img.shields.io/badge/docs-stable-brightgreen.svg?maxAge=2592000\" alt=\"Stable Docs\" /></a>\n\n### Related projects\n\n- [bukubrow](https://github.com/SamHH/bukubrow), WebExtension for browser integration\n- [oil](https://github.com/AndreiUlmeyda/oil), search-as-you-type cli front-end\n- [buku_run](https://github.com/carnager/buku_run), rofi front-end\n- [pinku](https://github.com/mosegontar/pinku), a Pinboard-to-buku import utility\n- [buku-dmenu](https://gitlab.com/benoliver999/buku-dmenu), a simple bash dmenu wrapper\n- [poku](https://github.com/shanedabes/poku), sync between Pocket and buku\n- [Ebuku](https://github.com/flexibeast/ebuku), Emacs interface to buku\n- [diigoku](https://github.com/dppdppd/diigoku), buku importer for Diigo\n- [BukuBot](https://git.xmpp-it.net/sch/BukuBot), Chat bot for XMPP with an extended visual interface\n\n\n### Videos\n\n- [Buku: Take Your Bookmarks Everywhere You Go](https://www.youtube.com/embed/9HzEHrUBQXE)\n- [Buku is a great open-source bookmark manager](https://www.youtube.com/embed/7VxgKMWm-J8)\n\n### In the Press\n\n- [2daygeek](http://www.2daygeek.com/buku-command-line-bookmark-manager-linux/)\n- [Hacker Milk](https://hackermilk.blogspot.com/2020/01/how-to-manage-your-browsers-bookmarks.html)\n- [It's F.O.S.S.](https://itsfoss.com/buku-command-line-bookmark-manager-linux/)\n- [LinOxide](https://linoxide.com/linux-how-to/buku-browser-bookmarks-linux/)\n- [LinuxUser Magazine 01/2017 Issue](http://www.linux-community.de/LU/2017/01/Das-Beste-aus-zwei-Welten)\n- [Make Tech Easier](https://www.maketecheasier.com/manage-browser-bookmarks-ubuntu-command-line/)\n- [One Thing Well](http://onethingwell.org/post/144952807044/buku)\n- [Open Source For You](https://opensourceforu.com/2018/05/buku-a-bookmark-manager-in-the-command-line/)\n- [ulno.net](https://ulno.net/blog/2017-07-19/of-bookmarks-tags-and-browsers/)\n"
  },
  {
    "path": "auto-completion/bash/buku-completion.bash",
    "content": "#\n# Bash completion definition for buku.\n#\n# Author:\n#   Arun Prakash Jana <engineerarun@gmail.com>\n#\n\n_buku () {\n    COMPREPLY=()\n    local IFS=$' \\n'\n    local cur=$2 prev=$3\n    local -a opts opts_with_args\n    opts=(\n        -a --add\n        --ai\n        -c --comment\n        --cached\n        --colors\n        -d --delete\n        --deep\n        --del-error\n        -e --export\n        --expand\n        --export-on\n        -f --format\n        -h --help\n        -i --import\n        --immutable\n        -j --json\n        -k --unlock\n        -l --lock\n        --markers\n        -n --count\n        --nc\n        --np\n        -o --open\n        --oa\n        --offline\n        --order\n        -p --print\n        -r --sreg\n        --random\n        --replace\n        -s --sany\n        -S --sall\n        --shorten\n        --suggest\n        --swap\n        -t --stag\n        --tacit\n        --tag\n        --tag-error\n        --tag-redirect\n        --threads\n        --title\n        -u --update\n        --url\n        --url-redirect\n        -V\n        -v --version\n        -w --write\n        -x --exclude\n        -g --debug\n    )\n    opts_with_arg=(\n        -a --add\n        --cached\n        --colors\n        -e --export\n        --expand\n        -f --format\n        -i --import\n        --immutable\n        -n --count\n        --order\n        -r --sreg\n        --replace\n        -s --sany\n        -S --sall\n        --shorten\n        --swap\n        --threads\n        --url\n        -x --exclude\n    )\n\n    # Do not complete non option names\n    [[ $cur == -* ]] || return 1\n\n    # Do not complete when the previous arg is an option expecting an argument\n    for opt in \"${opts_with_arg[@]}\"; do\n        [[ $opt == $prev ]] && return 1\n    done\n\n    # Complete option names\n    COMPREPLY=( $(compgen -W \"${opts[*]}\" -- \"$cur\") )\n    return 0\n}\n\ncomplete -F _buku buku\n"
  },
  {
    "path": "auto-completion/fish/buku.fish",
    "content": "#\n# Fish completion definition for buku.\n#\n# Author:\n#   Arun Prakash Jana <engineerarun@gmail.com>\n#\ncomplete -c buku -s a -l add     -r --description 'add bookmark'\ncomplete -c buku -l ai              --description 'auto-import bookmarks'\ncomplete -c buku -s c -l comment    --description 'comment on bookmark'\ncomplete -c buku -l cached       -r --description 'visit Wayback Machine cached version'\ncomplete -c buku -l colors       -r --description 'set output colors in 5-letter string'\ncomplete -c buku -s d -l delete     --description 'delete bookmark'\ncomplete -c buku -l deep            --description 'search matching substrings'\ncomplete -c buku -l del-error       --description 'delete bookmark on an HTTP error'\ncomplete -c buku -s e -l export  -r --description 'export bookmarks'\ncomplete -c buku -l expand       -r --description 'expand a tny.im shortened URL'\ncomplete -c buku -l export-on       --description 'export bookmarks based on HTTP status'\ncomplete -c buku -s f -l format  -r --description 'limit fields in print and JSON output'\ncomplete -c buku -s h -l help       --description 'show help'\ncomplete -c buku -s i -l import  -r --description 'import bookmarks'\ncomplete -c buku -l immutable    -r --description 'disable title update from web'\ncomplete -c buku -s j -l json       --description 'show JSON output for print and search'\ncomplete -c buku -s k -l unlock     --description 'decrypt database'\ncomplete -c buku -s l -l lock       --description 'encrypt database'\ncomplete -c buku -l markers         --description 'enable search-with-markers mode (.>:#*)'\ncomplete -c buku -s n -l count   -r --description 'results per page'\ncomplete -c buku -l nc              --description 'disable color output'\ncomplete -c buku -l np              --description 'non-interactive mode'\ncomplete -c buku -s o -l open       --description 'open bookmarks in browser'\ncomplete -c buku -l oa              --description 'browse all search results immediately'\ncomplete -c buku -l offline         --description 'add a bookmark without connecting to web'\ncomplete -c buku -l order        -r --description 'order by fields (+/- prefix for direction)'\ncomplete -c buku -s p -l print      --description 'show bookmark details'\ncomplete -c buku -s r -l sreg    -r --description 'match a regular expression'\ncomplete -c buku -l random          --description 'random subset (of 1 or given amount)'\ncomplete -c buku -l replace      -r --description 'replace a tag'\ncomplete -c buku -s s -l sany    -r --description 'match any keyword'\ncomplete -c buku -s S -l sall    -r --description 'match all keywords'\ncomplete -c buku -l shorten      -r --description 'shorten a URL using tny.im'\ncomplete -c buku -l suggest         --description 'show a list of similar tags'\ncomplete -c buku -l swap         -r --description 'swap 2 given bookmark indices'\ncomplete -c buku -s t -l stag       --description 'search by tag or show tags'\ncomplete -c buku -l tacit           --description 'reduce verbosity'\ncomplete -c buku -l tag             --description 'set tags, use + to append, - to remove'\ncomplete -c buku -l tag-error       --description 'add tag on an HTTP error'\ncomplete -c buku -l tag-redirect    --description 'add tag on a permanent redirect'\ncomplete -c buku -l threads      -r --description 'max connections for full refresh'\ncomplete -c buku -l title           --description 'set custom title'\ncomplete -c buku -s u -l update     --description 'update bookmark'\ncomplete -c buku -l url          -r --description 'set url'\ncomplete -c buku -l url-redirect    --description 'update URL on a permanent redirect'\ncomplete -c buku -s V               --description 'check latest upstream release'\ncomplete -c buku -s v -l version    --description 'show program version'\ncomplete -c buku -s w -l write      --description 'open editor'\ncomplete -c buku -s x -l exclude -r --description 'exclude keywords'\ncomplete -c buku -s g -l debug      --description 'enable debugging mode'\n"
  },
  {
    "path": "auto-completion/zsh/_buku",
    "content": "#compdef buku\n#\n# Completion definition for buku.\n#\n# Author:\n#   Arun Prakash Jana <engineerarun@gmail.com>\n#\n\nsetopt localoptions noshwordsplit noksharrays\nlocal -a args\nargs=(\n    '(-a --add)'{-a,--add}'[add bookmark]:URL tags'\n    '(--ai)--ai[auto-import bookmarks]'\n    '(-c --comment)'{-c,--comment}'[comment on bookmark]'\n    '(--cached)--cached[visit Wayback Machine cached version]:index/url'\n    '(--colors)--colors[set output colors in 5-letter string]:color string'\n    '(-d --delete)'{-d,--delete}'[delete bookmark]'\n    '(--deep)--deep[search matching substrings]'\n    '(--del-error)--del-error[delete bookmark on an HTTP error]::HTTP codes'\n    '(-e --export)'{-e,--export}'[export bookmarks]:html/md/db output file'\n    '(--expand)--expand[expand a tny.im shortened URL]:index/shorturl'\n    '(--export-on)--export-on[export bookmarks based on HTTP status]::HTTP codes'\n    '(-f --format)'{-f,--format}'[limit fields in print and JSON output]:value'\n    '(-h --help)'{-h,--help}'[show help]'\n    '(-i --import)'{-i,--import}'[import bookmarks]:html/md/db input file'\n    '(--immutable)--immutable[disable title update from web]:value'\n    '(-j --json)'{-j,--json}'[show JSON output for print and search]::file'\n    '(-k --unlock)'{-k,--unlock}'[decrypt database]'\n    '(-l --lock)'{-l,--lock}'[encrypt database]'\n    '(--markers)--markers[enable search-with-markers mode (.>:#*)]'\n    '(-n --count)'{-n,--count}'[results per page]:value'\n    '(--nc)--nc[disable color output]'\n    '(--np)--np[noninteractive mode]'\n    '(-o --open)'{-o,--open}'[open bookmarks in browser]'\n    '(--oa)--oa[browse all search results immediately]'\n    '(--offline)--offline[add a bookmark without connecting to web]'\n    '(--order)--order[order by fields (+/- prefix for direction)]:fields'\n    '(-p --print)'{-p,--print}'[show bookmark details]'\n    '(-r --sreg)'{-r,--sreg}'[match a regular expression]:regex'\n    '(--random)--random[random subset (of 1 or given amount)]::amount'\n    '(--replace)--replace[replace a tag]:tag to replace'\n    '(-s --sany)'{-s,--sany}'[match any keyword]:keyword(s)'\n    '(-S --sall)'{-S,--sall}'[match all keywords]:keyword(s)'\n    '(--shorten)--shorten[shorten a URL using tny.im]:index/url'\n    '(--suggest)--suggest[show a list of similar tags]'\n    '(--swap)--swap[swap 2 given bookmark indices]:index1 index2'\n    '(-t --stag)'{-t,--stag}'[search by tag or show tags]'\n    '(--tacit)--tacit[reduce verbosity]'\n    '(--tag)--tag[set tags, use + to append, - to remove]'\n    '(--tag-error)--tag-error[add tag on an HTTP error]::tag pattern'\n    '(--tag-redirect)--tag-redirect[add tag on a permanent redirect]::tag pattern'\n    '(--threads)--threads[max connections for full refresh]:value'\n    '(--title)--title[set custom title]'\n    '(-u --update)'{-u,--update}'[update bookmark]'\n    '(--url)--url[set url]:url'\n    '(--url-redirect)--url-redirect[update URL on a permanent redirect]'\n    '(-V)-V[check latest upstream release]'\n    '(-v --version)'{-v,--version}'[show program version]'\n    '(-w --write)'{-w,--write}'[open editor]'\n    '(-x --exclude)'{-x,--exclude}'[exclude keywords]:keyword(s)'\n    '(-g --debug)'{-g,--debug}'[enable debugging mode]'\n)\n_arguments -S -s $args\n"
  },
  {
    "path": "buku.1",
    "content": ".TH \"BUKU\" \"1\" \"07 Dec 2025\" \"Version 5.1\" \"User Commands\"\n.SH NAME\nbuku \\- Bookmark manager like a text-based mini-web\n.SH SYNOPSIS\n.B buku [OPTIONS] [KEYWORD [KEYWORD ...]]\n.SH DESCRIPTION\n.B buku\nis a command-line utility to store, tag, search and organize bookmarks.\n.PP\n.B Features\n.PP\n  * Store bookmarks with auto-fetched title, tags and description\n  * Auto-import from Firefox, Google Chrome, Chromium, Vivaldi, Brave, and MS Edge\n  * Open bookmarks and search results in browser\n  * Browse cached page from the Wayback Machine\n  * Text editor integration\n  * Lightweight, clean interface, custom colors\n  * Powerful search options (regex, substring...)\n  * Continuous search with on the fly mode switch\n  * Portable, merge-able database to sync between systems\n  * Import/export bookmarks from/to HTML, XBEL, Markdown, RSS/Atom or Orgfile\n  * Smart tag management using redirection (>>, >, <<)\n  * Multi-threaded full DB refresh\n  * Manual encryption support\n  * Shell completion scripts, man page with handy examples\n  * Privacy-aware (no unconfirmed user data collection)\n  * Can be used as a Python library\n  * Has a compation Web-application (Bukuserver) with an HTTP-based API (for personal use only)\n.SH OPERATIONAL NOTES\n.PP\n.IP 1. 4\nThe database file is stored in:\n  - \\fI$BUKU_DEFAULT_DBDIR/bookmarks.db\\fR, if BUKU_DEFAULT_DBDIR is defined (first preference), or\n  - \\fI$XDG_DATA_HOME/buku/bookmarks.db\\fR, if XDG_DATA_HOME is defined (second preference), or\n  - \\fI$HOME/.local/share/buku/bookmarks.db\\fR, if HOME is defined (third preference), or\n  - \\fI%APPDATA%\\\\buku\\\\bookmarks.db\\fR, if you are on Windows and APPDATA is defined (fourth preference), or\n  - the current directory.\n.PP\n.IP 2. 4\nIf the URL contains characters like ';', '&' or brackets they may be interpreted specially by the shell. To avoid it, add the URL within single or double quotes ('/\").\n.PP\n.IP 3. 4\nURLs are unique in DB. The same URL cannot be added twice.\n.PP\n.IP 4. 4\nBookmarks with immutable titles are listed with '(L)' after the title.\n.PP\n.IP 5. 4\n\\fBTags\\fR:\n  - Comma (',') is the tag delimiter in DB. A tag cannot have comma(s) in it. Tags are filtered (for unique tags) and sorted. Tags are stored in lower case and can be replaced, appended or deleted.\n  - Page keywords having a word to comma ratio > 3 are appended to description rather than tags.\n  - Parent folder (and subfolder) names are converted to all-lowercase tags during bookmarks HTML import.\n  - Releases prior to v2.7 support both capital and lower cases in tags. From v2.7 all tags are stored in lowercase. An undocumented option --fixtags is introduced to modify the older tags. It also fixes another issue where the same tag appears multiple times in the tagset of a record. Run \\fBbuku --fixtags\\fR once.\n  - Tags can be edited from the prompt very easily using '>>' (append), '>' (overwrite) and '<<' (remove) symbols. The LHS of the operands denotes the indices and ranges of tags to apply (as listed by --tag or key 't' at prompt) and the RHS denotes the actual DB indices and ranges of the bookmarks to apply the change to.\n.PP\n.IP 6. 4\n\\fBUpdate\\fR operation:\n  - If --title, --tag or --comment is passed without argument, clear the corresponding field from DB.\n  - If --url is passed (and --title is omitted), update the title from web using the URL. Description is updated (if --comment is omitted). Tags remain untouched.\n  - If indices are passed without any other options (--url, --title, --tag, --comment and --immutable), read the URLs from DB and update titles, description and append tags from web. Bookmarks marked immutable are skipped.\n  - Can update bookmarks matching a search, when combined with any of the search options and no arguments to update are passed.\n  - Additionally, --swap allows to modify records order (standalone operation).\n.PP\n.IP 7. 4\n\\fBDelete\\fR operation:\n  - When a record is deleted, the last record is moved to the index.\n  - Delete doesn't work with range and indices provided together as arguments. It's an intentional decision to avoid extra sorting, in-range checks and to keep the auto-DB compaction functionality intact. On the same lines, indices are deleted in descending order.\n  - Can delete bookmarks matching a search, when combined with any of the search options and no arguments to delete are passed.\n.PP\n.IP 8. 4\n\\fBSearch\\fR works in mysterious ways:\n  - Case-insensitive.\n  - Matches words in URL, title and tags.\n  - --sany : match any of the keywords in URL, title, description or tags. Default search option.\n  - --sall : match all the keywords in URL, title, description or tags.\n  - --deep : match \\fBsubstrings\\fR (`match` matches `rematched`) in URL, title, description and tags.\n  - --markers : match each keyword to a \\fBspecific\\fR field, depending on its prefix.\n  - --sreg : match a regular expression (ignores --deep).\n  - --stag : search bookmarks by tags, or list all tags alphabetically with usage count (if no arguments). Delimit the list of tags in the query with `,` to search for bookmarks that match ANY of the listed tags. Delimit tags with `+` to search for bookmarks that match ALL of the listed tags. Note that `,` and `+` cannot be used together in the same search. Exclude bookmarks matching certain tags from the results by using ` - ` followed by the tags. Note that the ` - ` operator and the ` + ` delimiter must be space separated: ` - ` instead of `-` and ` + ` instead of `+`. This is to distinguish them from hyphenated tags (e.g., `some-tag-name`) and tags with '+'s (e.g., `some+tag+name`).\n  - Search for keywords along with tag filtering is possible. Two searches are issued (one for keywords and another for tags) and the intersection of the 2 sets is returned as the resultset.\n  - Search results are indexed incrementally. This index is different from actual database index of a bookmark record which is shown within '[]' after the title.\n  - Results for \\fIany\\fR keyword matches are ordered by the number of keyword matches - results matching more keywords (\\fImatch score\\fR) will appear earlier in the list. Results having the same number of matches will be ranked by their record DB index. If only one keyword is searched, results will be ordered by DB index, since all matching records will have the same \\fImatch score\\fR.\n  - Sorting order can be specified (for matches with same amount of matched keywords, if relevant). This option also works with regular printing/export.\n.PP\n.IP 9. 4\n\\fBImport\\fR:\n  - Auto-import looks in the default installation path and default user profile.\n  - URLs starting with `place:`, `file://` and `apt:` are ignored during import.\n  - Parent folder (and subfolder) names are automatically imported as tags if --tacit is used.\n  - Tags are merged even if bookmark URL exists when --tacit is used.\n  - JSON files exported from browser can be imported. Export to JSON is not supported.\n.PP\n.IP 10. 4\n\\fBEncryption\\fR is optional and manual. AES256 algorithm is used. To use encryption, the database file should be unlocked (-k) before using \\fBbuku\\fR and locked (-l) afterwards. Between these 2 operations, the database file lies unencrypted on the disk, and NOT in memory. Also, note that the database file is \\fBunencrypted on creation\\fR.\n.PP\n.IP 11. 4\n\\fBEditor\\fR support:\n  - A single bookmark can be edited before adding. The editor can be set using the environment variable *EDITOR* or by explicitly specifying the editor. The latter takes precedence. If -a is used along with -w, the details are populated in the editor template.\n  - In case of edit and update (a single bookmark), the existing record details are fetched from DB and populated in the editor template. The environment variable EDITOR must be set. Note that -u works independently of -w.\n  - All lines beginning with \"#\" will be stripped. Then line 1 will be treated as the URL, line 2 will be the title, line 3 will be comma separated tags, and the rest of the lines will be parsed as descriptions.\n.PP\n.IP 12. 4\n\\fBProxy\\fR support: please refer to the \\fBENVIRONMENT\\fR section.\n.PP\n.IP 13. 4\n\\fBAlternative DB file\\fR:\n  - The option \\fB--db\\fR (to specify an alternative database file location) is app-only. Manual usage is prone to issues arising from human error.\n  - Note that this option is useful if you want to store the db file in cloud synced location. Another mechanism could be to have the db file synced and create a symlink to it at the default location.\n  - When the argument to \\fB--db\\fR contains neither `.` nor directory separator, it's considered a \\fIname\\fR and is resolved as the matching file with `.db` extension within the default DB directory.\n  - When invoked specifically as \\fBbuku --db\\fR, the program prints out the list of DB names in the default DB directory.\n  - When running Bukuserver (webUI), alternative DB file can be specified via \\fBBUKUSERVER_DB_FILE\\fR environment variable. Additionally, the Bukuserver runner script supports switching between DB files within the default Buku DB folder.\n  - In the interactive shell mode, the \\fBDB\\fR command can be used to similarly switch between DB files by name. (You can use non-standard extensions by specifying them, and switch directories by specifying a path – absolute or relative to the current DB. \\fB~.\\fR stands for the default database.)\n.SH GENERAL OPTIONS\n.TP\n.BI \\-a \" \" \\--add \" URL [+|-] [tag, ...]\"\nBookmark\n.I URL\nalong with comma-separated tags. A tag can have multiple words. (These tags \\fBoverride\\fR fetched tags, unless preceded with '+' or '-'.)\n.TP\n.BI \\-u \" \" \\--update \" [...]\"\nUpdate fields of the bookmarks at specified indices in DB. If no arguments are specified, all titles and descriptions are refreshed from the web. Tags remain untouched. Works with update modifiers for the fields url, title, tag and comment. If only indices are passed without any edit options, titles and descriptions are fetched and updated (if not empty). Accepts hyphenated ranges and space-separated indices. Updates search results when used with search options, if no arguments.\n.TP\n.BI \\-w \" \" \\--write \" [editor|index]\"\nEdit a bookmark in\n.I editor\nbefore adding it. To edit and update an existing bookmark, the\n.I index\nshould be passed. In this case the environment variable EDITOR must be set. The last record is opened in EDITOR if index=-1.\n.TP\n.BI \\-d \" \" \\--delete \" [...]\"\nDelete bookmarks. Accepts space-separated list of indices (e.g. 5 6 23 4 110 45) or a single hyphenated range (e.g. 100-200). Note that range and list don't work together. Deletes search results when combined with search options, if no arguments.\n.TP\n.BI \\--retain-order\nWhen deleting bookmarks, shift indices of multiple records instead of replacing the deleted record with the last one.\n.TP\n.BI \\-v \" \" \\--version\nShow program version and exit.\n.TP\n.BI \\-h \" \" \\--help\nShow program help and exit.\n.SH EDIT OPTIONS\n.TP\n.BI \\--url \" [...]\"\nSpecify the URL, works with --update only. Fetches and updates title if --title is not used.\n.TP\n.BI \\--tag \" [+|-] [...]\"\nSpecify comma separated tags, works with --add, --update. Clears the tags, if no arguments passed. Appends or deletes tags, if list of tags is preceded by '+' or '-' respectively.\n.TP\n.BI \\--title \" [...]\"\nManually specify the title, works with --add, --update. Omits or clears the title, if no arguments passed.\n.TP\n.BI \\-c \" \" \\--comment \" [...]\"\nAdd notes or description of the bookmark, works with --add, --update. Clears the comment, if no arguments passed.\n.TP\n.BI \\--immutable \" N\"\nSet the title, description and tags of a bookmark immutable during autorefresh. Works with --add, --update. N=1 sets the immutable flag, N=0 removes it. If omitted, bookmarks are added with N=0.\n.TP\n.BI \\--swap \" N M\"\nSwap two records at specified indices. This is a standalone operation (cannot be invoked along with any other).\n.SH SEARCH OPTIONS\n.TP\n.BI \\-s \" \" \\--sany \" keyword [...]\"\nSearch bookmarks with ANY of the keyword(s) in URL, title or tags and show the results. Prompts to enter result number to open in browser. Note that the sequential result index is not the DB index. The DB index is shown within '[]' after the title.\n.br\nThis is the default search option for positional arguments if no other search option is specified.\n.TP\n.BI \\-S \" \" \\--sall \" keyword [...]\"\nSearch bookmarks with ALL keywords in URL, title or tags and show the results. Behaviour same as --sany.\n.br\nSpecial keywords:\n.br\n\"blank\": list entries with empty title/tag\n.br\n\"immutable\": list entries with locked title\n.br\nNOTE: To search the keywords, use --sany\n.TP\n.BI \\--deep\nSearch modifier to match substrings. Works with --sany, --sall.\n.TP\n.BI \\--markers\nSearch modifier to match specific fields based on (optional) prefix markers (i.e. beginning of the keyword):\n  - '.' : search in title\n  - '>' : search in description\n  - ':' : search in URL\n  - '#' : search in tags (comma-separated, \\fBpartial\\fR matches; not affected by --deep)\n  - '#,' : search in tags (comma-separated, \\fBexact\\fR matches; not affected by --deep)\n  - '*' : search in all fields (same as no prefix)\n.TP\n.BI \\-r \" \" \\--sreg \" expression\"\nScan for a regular expression match.\n.TP\n.BI \\-t \" \" \\--stag \" [tag [,|+] ...] [\\- tag, ...]\"\nSearch bookmarks by tags.\n.br\nUse ',' delimiter to find entries matching ANY of the tags\n.br\nUse ' + ' delimiter to find entries matching ALL of the tags. (Note that the ' + ' delimiter must be space separated)\n.br\nNOTE: Cannot combine ',' and '+' in the same search\n.br\nUse ' - ' to exclude bookmarks that match the tags that follow. (Note that the '-' operator must be space separated).\n.br\nList all tags alphabetically, if no arguments. The usage count (number of bookmarks having the tag) is shown within first brackets.\n.TP\n.BI \\-x \" \" \\--exclude \" keyword [...]\"\nExclude bookmarks matching the specified keywords. Works with --sany, --sall, --sreg and --stag.\n.TP\n.BI \\--random \" [N]\"\nOutput random bookmarks out of the selection (1 unless amount is specified).\n.TP\n.BI \\--order \" fields [...]\"\nOrder printed/exported records by the given fields (from DB or JSON) and/or netloc. You can specify sort direction for each by prepending '+'/'-' (default is '+').\n.SH ENCRYPTION OPTIONS\n.TP\n.BI \\-l \" \" \\--lock \" [N]\"\nEncrypt (lock) the DB file with\n.I N\n(> 0, default 8) hash passes to generate key.\n.TP\n.BI \\-k \" \" \\--unlock \" [N]\"\nDecrypt (unlock) the DB file with\n.I N\n(> 0, default 8) hash passes to generate key.\n.SH POWER OPTIONS\n.TP\n.BI \\--ai\nAuto-import bookmarks from Firefox, Google Chrome, Chromium, Vivaldi, Brave, and MS Edge browsers. (Firefox profile can be specified using environment variable FIREFOX_PROFILE.)\n.TP\n.BI \\-e \" \" \\--export \" file\"\nExport bookmarks to Firefox bookmarks formatted HTML. Works with all search options.\n.br\nXBEL is used if\n.I file\nhas extension '.xbel'.\n.br\nMarkdown is used if\n.I file\nhas extension '.md'. Markdown format: [title](url), 1 entry per line.\n.br\nOrgfile is used if\n.I file\nhas extension '.org' Orgfile format: * [[url][title]], 1 entry per line.\n.br\nRSS is used if\n.I file\nhas extension '.rss'/'.atom' RSS format: <entry> per bookmark with <title>, <link>, <category>, <content> elements\n.br\nA buku database is generated if\n.I file\nhas extension '.db'.\n.TP\n.BI \\-i \" \" \\--import \" file\"\nImport bookmarks from Firefox bookmarks formatted HTML.\n.I file\nis considered Firefox-exported JSON if it has '.json' extension, XBEL if it is '.xbel', Markdown (compliant with --export format) if it is '.md', Orgfile if the extension is '.org', RSS if the extension is '.rss'/'.atom' or another buku database if the extension is '.db'.\n.TP\n.BI \\-p \" \" \\--print \" [...]\"\nShow details (DB index, URL, title, tags and comment) of bookmark record by DB index. If no arguments, all records with actual index from DB are shown. Accepts hyphenated ranges and space-separated indices. A negative value (introduced for convenience) behaves like the tail utility, e.g., -n shows the details of the last n bookmarks.\n.TP\n.BI \\-f \" \" \\--format \" N\"\nShow selective monochrome output with specific fields. Works with --print. Search results honour the option when used along with --json. Useful for creating batch scripts.\n.br\n.I N\n= 1, show only URL.\n.br\n.I N\n= 2, show URL and tags in a single line.\n.br\n.I N\n= 3, show only title.\n.br\n.I N\n= 4, show URL, title and tags in a single line.\n.br\n.I N\n= 5, show title and tags in a single line.\n.br\nTo omit DB index from printed results, use N0, e.g., 10, 20, 30, 40, 50.\n.TP\n.BI \\-j \" \" \\--json\nOutput data formatted as JSON, works with --print output and search results.\n.TP\n.BI \\--colors \" COLORS\"\nSet output colors. Refer to the \\fBCOLORS\\fR section below for details.\n.TP\n.BI \\--nc\nDisable color output in all messages. Useful on terminals which can't handle ANSI color codes or scripted environments.\n.TP\n.BI \\-n \" \" \\--count \" N\"\nNumber of results to show per page (default 10).\n.TP\n.BI \\--np\nDo not show the prompt, run and exit.\n.TP\n.BI \\-o \" \" \\--open \" [...]\"\nOpen bookmarks by DB indices or ranges in browser. Open a random index if argument is omitted.\n.TP\n.BI \\--oa\nOpen all search results immediately in the browser. Works best with --np. When used along with --update or --delete, URLs are opened in the browser first and then modified or deleted.\n.TP\n.BI \\--default-scheme \" scheme\"\nWhen opening a bookmark without a scheme in its URI, use this scheme (default http).\n.TP\n.BI \\--replace \" old new\"\nReplace\n.I old\ntag with\n.I new\ntag if both are passed; delete\n.I old\ntag if\n.I new\ntag is not specified.\n.TP\n.BI \\--url-redirect\nwhen fetching an URL, use the resulting URL from following \\fBpermanent\\fR redirects (when combined with --export, the old URL is included as additional metadata).\n.TP\n.BI \\--tag-redirect \" [tag]\"\nwhen fetching an URL that causes permanent redirect, add a\n.I tag\nin specified pattern (using 'http:{}' if not specified).\n.TP\n.BI \\--tag-error \" [tag]\"\nwhen fetching an URL that causes an HTTP error, add a\n.I tag\nin specified pattern (using 'http:{}' if not specified).\n.TP\n.BI \\--del-error \" [...]\"\nwhen fetching an URL causes any (given) HTTP error, delete/do not add it. (Use a parameter like '404' or '400-404,500')\n.TP\n.BI \\--export-on \" [...]\"\nexport records affected by the above options, including removed info (requires --update and --export; specific HTTP response filter can be provided).\n.TP\n.BI \\--reorder \" order...\"\nupdate DB indices to match specified order (specified the same way as for --order)\n.TP\n.BI \\--cached \" index|URL\"\nBrowse the latest cached version of the URL at DB\n.I index\nor an independent\n.I URL\nusing the Wayback Machine. Useful for viewing the content of bookmarks which are not live any more.\n.TP\n.BI \\--offline\nAdd a bookmark without connecting to the web.\n.TP\n.BI \\--suggest\nShow a list of similar tags to choose from when adding a new bookmark.\n.TP\n.BI \\--tacit\nShow lesser output. Reduces the verbosity of certain operations like add, update etc.\n.TP\n.BI \\--nostdin\nDo not attempt to read data from standard input e.g. when the program is not executed from a tty.\n.TP\n.BI \\--threads\nMaximum number of parallel network connection threads to use during full DB refresh. By default 4 connections are spawned.\n.I N\ncan range from 1 to 10.\n.TP\n.BI \\-V\nCheck the latest upstream version available. This is FYI. It is possible the latest upstream released version is still not available in your package manager as the process takes a while.\n.TP\n.BI \\-g \" \" \\--debug\nShow debug information and additional logs.\n.SH PROMPT KEYS\n.TP\n.BI \"1-N\"\nBrowse search results by indices and ranges.\n.TP\n.BI \"O\" \" [id|range [...]]\"\nTry to open search results or indices (when not in a search context) in a GUI browser. Toggle try to open urls in a GUI based browser (even if BROWSER is set) if no arguments. Toggling is useful when trying to open bookmarks by DB index.\n.TP\n.BI \"a\"\nOpen all search results in browser.\n.TP\n.BI \"s\" \" keyword [...]\"\nSearch for records with ANY keyword.\n.TP\n.BI \"S\" \" keyword [...]\"\nSearch for records with ALL keywords.\n.TP\n.BI \"d\"\nToggle deep search to match substrings ('pen' matches 'opened').\n.TP\n.BI \"m\"\nSearch with markers - search string is split into keywords by prefix markers, which determine what field the keywords is searched in:\n  - '.', '>' or ':' - title, description or URL\n  - '#'/'#,' - tags (comma-separated, partial/full match)\n  - '*' - all fields (can be omitted in the 1st keyword)\n\nNote: tag marker is not affected by \\fBd\\fR (deep search)\n.TP\n.BI \"v\" \" fields\"\nChange sorting order (default is '+index'). Multiple comma/space separated fields can be specified.\n.TP\n.BI \"v!\" \" fields\"\nUpdate indices in DB to match specified order.\n.TP\n.BI \"r\" \" expression\"\nRun a regular expression search.\n.TP\n.BI \"t\" \" [...]\"\nSearch bookmarks by a tag. List all tags alphabetically, if no arguments.\n.TP\n.BI \"g\" \" taglist id|range [...] [>>|>|<<] [record id|range ...]\"\nAppend, set, remove specific or all tags by indices and/or ranges to bookmark indices and/or ranges (see \\fBEXAMPLES\\fR section below). Search by space-separated taglist id(s) and/or range if records are omitted.\n.TP\n.BI \"n\"\nDisplay the next page of search results.\n.TP\n.BI \"N\"\nDisplay the previous page of search results.\n.TP\n.BI \"o\" \" id|range [...]\"\nBrowse bookmarks by indices and/or ranges.\n.TP\n.BI \"p\" \" id|range [...]\"\nPrint bookmarks by indices and/or ranges.\n.TP\n.BI \"w\" \" [editor|id]\"\nEdit and add or update a bookmark.\n.TP\n.BI \"c id\"\nCopy url at search result index to clipboard.\n.TP\n.BI \"DB\" \" [name]\"\nIf used without \\fBname\\fR, display list of available DBs (files with '.db' extension in the folder of the current DB). If used with \\fBname\\fR, switch to the specified DB.\nYou can omit file extension ('.db' will be used), and you can specify a path instead in order to switch a folder (if selected path is a folder, default filename is assumed).\nIf the specified DB file doesn't exist, it will be created. Note: you can use '~.' as a shortcut for default DB.\n.TP\n.BI \"?\"\nShow help on prompt keys.\n.TP\n.BI \"q, ^D, double Enter\"\nExit buku.\n.SH ENVIRONMENT\n.TP\n.BI \"Completion scripts\"\nShell completion scripts for Bash, Fish and Zsh can be found in:\n.br\n.I https://github.com/jarun/buku/blob/master/auto-completion\n.TP\n.BI BROWSER\nOverrides the default browser. Refer to:\n.br\n.I http://docs.python.org/library/webbrowser.html\n.TP\n.BI EDITOR\nIf defined, will be used as the editor to edit bookmarks with option --write.\n.TP\n.BI https_proxy\nIf defined, will be used to access http and https resources through the configured proxy. Supported format:\n.br\nhttp[s]://[username:password@]proxyhost:proxyport/\n.TP\n.BI \"GUI integration\"\n.B buku\ncan be integrated in a GUI environment with simple tweaks. Please refer to:\n.br\n.I https://github.com/jarun/buku/wiki/System-integration\n.SH COLORS\n\\fBbuku\\fR allows you to customize the color scheme via a five-letter string, reminiscent of BSD \\fBLSCOLORS\\fR. The five letters represent the colors of\n.IP - 2\nindex\n.PD 0 \\\" Change paragraph spacing to 0 in the list\n.IP - 2\ntitle\n.IP - 2\nURL\n.IP - 2\ndescription/comment/note\n.IP - 2\ntag\n.PD 1 \\\" Restore paragraph spacing\n.TP\nrespectively. The five-letter string is passed is as the argument to the \\fB--colors\\fR option, or as the value of the environment variable \\fBBUKU_COLORS\\fR.\n.TP\nWe offer the following colors/styles:\n.TS\ntab(;) box;\nl|l\n-|-\nl|l.\nLetter;Color/Style\na;black\nb;red\nc;green\nd;yellow\ne;blue\nf;magenta\ng;cyan\nh;white\ni;bright black\nj;bright red\nk;bright green\nl;bright yellow\nm;bright blue\nn;bright magenta\no;bright cyan\np;bright white\nA-H;bold version of the lowercase-letter color\nI-P;bold version of the lowercase-letter bright color\nx;normal\nX;bold\ny;reverse video\nY;bold reverse video\n.TE\n.TP\n.TP\nThe default colors string is \\fIoKlxm\\fR, which stands for\n.IP - 2\nbright cyan index\n.PD 0 \\\" Change paragraph spacing to 0 in the list\n.IP - 2\nbold bright green title\n.IP - 2\nbright yellow URL\n.IP - 2\nnormal description\n.IP - 2\nbright blue tag\n.PD 1 \\\" Restore paragraph spacing\n.TP\nNote that\n.IP - 2\nBright colors (implemented as \\\\x1b[90m - \\\\x1b[97m) may not be available in all color-capable terminal emulators;\n.IP - 2\nSome terminal emulators draw bold text in bright colors instead;\n.IP - 2\nSome terminal emulators only distinguish between bold and bright colors via a default-off switch.\n.TP\nPlease consult the manual of your terminal emulator as well as \\fIhttps://en.wikipedia.org/wiki/ANSI_escape_code\\fR for details.\n\n.SH EXAMPLES\n.PP\n.IP 1. 4\n\\fBEdit and add\\fR a bookmark from editor:\n.PP\n.EX\n.IP\n.B buku -w\n.br\n.B buku -w 'gedit -w'\n.br\n.B buku -w 'macvim -f' -a https://ddg.gg search engine, privacy\n.EE\n.PP\n.IP \"\" 4\nThe first command picks editor from the environment variable \\fIEDITOR\\fR. The second command opens gedit in blocking mode. The third command opens macvim with option -f and the URL and tags populated in template.\n.PP\n.IP 2. 4\n\\fBAdd\\fR a simple bookmark:\n.PP\n.EX\n.IP\n.B buku --nostdin -a https://github.com/\n.EE\n.PP\n.IP \"\" 4\nIn the output, >: url, +: comment, #: tags.\n.PP\n.IP 3. 4\n\\fBAdd\\fR a bookmark with \\fBtags\\fR 'search engine' and 'privacy', \\fBcomment\\fR 'Search engine with perks', \\fBfetch page title\\fR from the web:\n.PP\n.EX\n.IP\n.B buku -a https://ddg.gg search engine, privacy -c Search engine with perks\n.EE\n.PP\n.IP \"\" 4\nIn the output, >: url, +: comment, #: tags.\n.PP\n.IP 4. 4\n\\fBAdd\\fR a bookmark with tags 'search engine' & 'privacy' and \\fBimmutable custom title\\fR 'DDG':\n.PP\n.EX\n.IP\n.B buku -a https://ddg.gg search engine, privacy --title 'DDG' --immutable 1\n.EE\n.PP\n.IP \"\" 4\nNote that URL must precede tags.\n.PP\n.IP 5. 4\n\\fBAdd\\fR a bookmark \\fBwithout a title\\fR (works for update too):\n.PP\n.EX\n.IP\n.B buku -a https://ddg.gg search engine, privacy --title\n.EE\n.PP\n.IP 6. 4\n\\fBEdit and update\\fR a bookmark from editor:\n.PP\n.EX\n.IP\n.B buku -w 15012014\n.EE\n.PP\n.IP \"\" 4\nThis will open the existing bookmark's details in the editor for modifications. Environment variable \\fIEDITOR\\fR must be set.\n.PP\n.IP 7. 4\n\\fBUpdate\\fR existing bookmark at index 15012014 with new URL, tags and comments, fetch title from the web:\n.PP\n.EX\n.IP\n.B buku -u 15012014 --url http://ddg.gg/ --tag web search, utilities -c Private search engine\n.EE\n.PP\n.IP 8. 4\n\\fBFetch and update only title\\fR for bookmark at 15012014:\n.PP\n.EX\n.IP\n.B buku -u 15012014\n.EE\n.PP\n.IP 9. 4\n\\fBUpdate only comment\\fR for bookmark at 15012014:\n.PP\n.EX\n.IP\n.B buku -u 15012014 -c this is a new comment\n.EE\n.PP\n.IP \"\" 4\nApplies to --url, --title and --tag too.\n.PP\n.IP 10. 4\n\\fBExport\\fR bookmarks tagged 'tag 1' or 'tag 2' to HTML, XBEL, Markdown, Orgfile or a new database:\n.PP\n.EX\n.IP\n.B buku -e bookmarks.html --stag tag 1, tag 2\n.br\n.B buku -e bookmarks.xbel --stag tag 1, tag 2\n.br\n.B buku -e bookmarks.md --stag tag 1, tag 2\n.br\n.B buku -e bookmarks.org --stag tag 1, tag 2\n.br\n.B buku -e bookmarks.db --stag tag 1, tag 2\n.EE\n.PP\n.IP \"\" 4\nAll bookmarks are exported if search is not opted.\n.PP\n.IP 11. 4\n\\fBImport\\fR bookmarks from HTML, XBEL, Markdown or Orgfile:\n.PP\n.EX\n.IP\n.B buku -i bookmarks.html\n.br\n.B buku -i bookmarks.xbel\n.br\n.B buku -i bookmarks.md\n.br\n.B buku -i bookmarks.db\n.EE\n.PP\n.IP 12. 4\n\\fBDelete only comment\\fR for bookmark at 15012014:\n.PP\n.EX\n.IP\n.B buku -u 15012014 -c\n.EE\n.PP\n.IP \"\" 4\nApplies to --title and --tag too. URL cannot be deleted without deleting the bookmark.\n.PP\n.IP 13. 4\n\\fBUpdate\\fR or refresh \\fBfull DB\\fR with page titles from the web:\n.PP\n.EX\n.IP\n.B buku -u\n.br\n.B buku -u --tacit (show only failures and exceptions)\n.EE\n.PP\n.IP \"\" 4\nThis operation can update the title or description fields of non-immutable bookmarks by parsing the fetched page. Fields are updated only if the fetched fields are non-empty. Tags remain untouched.\n.PP\n.IP 14. 4\n\\fBDelete\\fR bookmark at index 15012014:\n.PP\n.EX\n.IP\n.B buku -d 15012014\n.EE\n.PP\n.IP \"\" 4\nThe last index is moved to the deleted index to keep the DB compact. Add --tacit to delete without confirmation.\n.PP\n.IP 15. 4\n\\fBDelete all\\fR bookmarks:\n.PP\n.EX\n.IP\n.B buku -d\n.EE\n.PP\n.IP 16. 4\n\\fBDelete\\fR a \\fBrange or list\\fR of bookmarks:\n.PP\n.EX\n.IP\n.B buku -d 100-200\n.br\n.B buku -d 100 15 200\n.EE\n.PP\n.IP 17. 4\n\\fBSearch\\fR bookmarks for \\fBANY\\fR of the keywords 'kernel' and 'debugging' in URL, title or tags:\n.PP\n.EX\n.IP\n.B buku kernel debugging\n.br\n.B buku -s kernel debugging\n.EE\n.PP\n.IP 18. 4\n\\fBSearch\\fR bookmarks with \\fBALL\\fR the keywords 'kernel' and 'debugging' in URL, title or tags:\n.PP\n.EX\n.IP\n.B buku -S kernel debugging\n.EE\n.PP\n.IP 19. 4\n\\fBSearch\\fR bookmarks \\fBtagged\\fR 'general kernel concepts':\n.PP\n.EX\n.IP\n.B buku --stag general kernel concepts\n.EE\n.PP\n.IP 20. 4\n\\fBSearch\\fR for bookmarks matching \\fBANY\\fR of the tags 'kernel', 'debugging', 'general kernel concepts':\n.PP\n.EX\n.IP\n.B buku --stag kernel, debugging, general kernel concepts\n.EE\n.PP\n.IP 21. 4\n\\fBSearch\\fR for bookmarks matching \\fBALL\\fR of the tags 'kernel', 'debugging', 'general kernel concepts':\n.PP\n.EX\n.IP\n.B buku --stag kernel + debugging + general kernel concepts\n.EE\n.PP\n.IP 22. 4\n\\fBSearch\\fR for bookmarks matching any of the keywords 'hello' or 'world', excluding the keywords 'real' and 'life', matching both the tags 'kernel' and 'debugging', but \\fBexcluding\\fR the tags 'general kernel concepts' and 'books':\n.PP\n.EX\n.IP\n.B buku hello world --exclude real life --stag 'kernel + debugging - general kernel concepts, books'\n.EE\n.PP\n.IP 23. 4\n\\fBSearch\\fR for bookmarks with different tokens for each field, and print them out sorted by the tags (ascending) and URL (descending)\n.PP\n.EX\n.IP\n.B buku --order +tags,-url --markers --sall 'global substring' '.title substring' ':url substring' :https '> description substring' '#partial,tags:' '#,exact,tags' '*another global substring'\n.EE\n.PP\n.IP 24. 4\nList \\fBall unique tags\\fR alphabetically:\n.PP\n.EX\n.IP\n.B buku --stag\n.EE\n.PP\n.IP 25. 4\nRun a \\fBsearch and update\\fR the results:\n.PP\n.EX\n.IP\n.B buku -s kernel debugging -u --tag + linux kernel\n.EE\n.PP\n.IP 26. 4\nRun a \\fBsearch and delete\\fR the results:\n.PP\n.EX\n.IP\n.B buku -s kernel debugging -d\n.EE\n.PP\n.IP 27. 4\n\\fBEncrypt or decrypt\\fR DB with \\fBcustom number of iterations\\fR (15) to generate key:\n.PP\n.EX\n.IP\n.B buku -l 15\n.br\n.B buku -k 15\n.EE\n.PP\n.IP \"\" 4\nThe same number of iterations must be specified for one lock & unlock instance. Default is 8, if omitted.\n.PP\n.IP 28. 4\n\\fBShow details\\fR of bookmarks at index 15012014 and ranges 20-30, 40-50:\n.PP\n.EX\n.IP\n.B buku -p 20-30 15012014 40-50\n.EE\n.PP\n.IP 29. 4\nShow details of the \\fBlast 10 bookmarks\\fR:\n.PP\n.EX\n.IP\n.B buku -p -10\n.EE\n.PP\n.IP 30. 4\n\\fBShow all\\fR bookmarks with real index from database:\n.PP\n.EX\n.IP\n.B buku -p\n.br\n.B buku -p | more\n.EE\n.PP\n.IP 31. 4\n\\fBReplace tag\\fR 'old tag' with 'new tag':\n.PP\n.EX\n.IP\n.B buku --replace 'old tag' 'new tag'\n.EE\n.PP\n.IP 32. 4\n\\fBDelete tag\\fR 'old tag' from DB:\n.PP\n.EX\n.IP\n.B buku --replace 'old tag'\n.EE\n.PP\n.IP 33. 4\n\\fBAppend (or delete) tags\\fR 'tag 1', 'tag 2' to (or from) existing tags of bookmark at index 15012014:\n.PP\n.EX\n.IP\n.B buku -u 15012014 --tag + tag 1, tag 2\n.br\n.B buku -u 15012014 --tag - tag 1, tag 2\n.EE\n.PP\n.IP 34. 4\n\\fBOpen URL\\fR at index 15012014 in browser:\n.PP\n.EX\n.IP\n.B buku -o 15012014\n.EE\n.PP\n.IP 35. 4\nList bookmarks with \\fBno title or tags\\fR for bookkeeping:\n.PP\n.EX\n.IP\n.B buku -S blank\n.EE\n.PP\n.IP 36. 4\nList bookmarks with \\fBimmutable title\\fR:\n.PP\n.EX\n.IP\n.B buku -S immutable\n.EE\n.PP\n.IP 37. 4\n\\fBAppend, remove tags at prompt\\fR (taglist index to the left, bookmark index to the right):\n.PP\n.EX\n.IP\n// append tags at taglist indices 4 and 6-9 to existing tags in bookmarks at indices 5 and 2-3\n.br\n.B buku (? for help) g 4 9-6 >> 5 3-2\n.br\n// set tags at taglist indices 4 and 6-9 as tags in bookmarks at indices 5 and 2-3\n.br\n.B buku (? for help) g 4 9-6 > 5 3-2\n.br\n// remove all tags from bookmarks at indices 5 and 2-3\n.br\n.B buku (? for help) g > 5 3-2\n.br\n// remove tags at taglist indices 4 and 6-9 from tags in bookmarks at indices 5 and 2-3\n.br\n.B buku (? for help) g 4 9-6 << 5 3-2\n.EE\n.PP\n.IP 38. 4\nList bookmarks with \\fBcolored output\\fR:\n.PP\n.EX\n.IP\n.B $ buku --colors oKlxm -p\n.EE\n.PP\n.IP 39. 4\nAdd a bookmark after following all permanent redirects, but only if the server doesn't respond with an error (and there's no network failure)\n.PP\n.EX\n.IP\n.B buku --add http://wikipedia.net --url-redirect --del-error\n.br\n2. Wikipedia\n.br\n   > https://www.wikipedia.org/\n.br\n   + Wikipedia is a free online encyclopedia, created and edited by volunteers around the world and hosted by the Wikimedia Foundation.\n.EE\n.PP\n.IP 40. 4\nAdd a bookmark with tag 'http redirect' if the server responds with a permanent redirect, or tag shaped like 'http 404' on an error response:\n.PP\n.EX\n.IP\n.B buku --add http://wikipedia.net/notfound --tag-redirect 'http redirect' --tag-error 'http {}'\n.br\n[ERROR] [404] Not Found\n.br\n3. Not Found\n.br\n   > http://wikipedia.net/notfound\n.br\n   # http 404,http redirect\n.EE\n.PP\n.IP 41. 4\nUpdate all bookmarks matching the search by updating the URL if the server responds with a permanent redirect, deleting the bookmark if the server responds with HTTP error 400, 401, 402, 403, 404 or 500, or adding a tag shaped like 'http:{}' in case of any other HTTP error; then export those affected by such changes into an HTML file, marking deleted records as well as old URLs for those replaced by redirect.\n.PP\n.EX\n.IP\n.B buku -S ://wikipedia.net -u --url-redirect --tag-error --del-error 400-404,500 --export-on --export backup.html\n.EE\n.PP\n.IP 42. 4\nPrint out a single \\fBrandom\\fR bookmark:\n.PP\n.EX\n.IP\n.B buku --random\n.EE\n.PP\n.IP 43. 4\nPrint out 3 \\fBrandom\\fR bookmarks \\fBordered\\fR by netloc (reversed), title and url:\n.PP\n.EX\n.IP\n.B buku --random 3 --order ,-netloc,title,+url\n.EE\n.PP\n.IP 44. 4\nPrint out a single \\fBrandom\\fR bookmark matching \\fBsearch\\fR criteria, and \\fBexport\\fR into a Markdown file (in DB order):\n.PP\n.EX\n.IP\n.B buku --random -S kernel debugging --export random.md\n.EE\n.PP\n.IP 45. 4\nSwap positions of records #4 and #5:\n.PP\n.EX\n.IP\n.B buku --swap 4 5\n.EE\n.PP\n.IP 46. 4\nUpdate indices in all bookmarks to match specified order:\n.PP\n.EX\n.IP\n.B buku --reorder ,-netloc,title,+url\n.EE\n.PP\n\n\n.SH AUTHOR\nArun Prakash Jana <engineerarun@gmail.com>\n.SH HOME\n.I https://github.com/jarun/buku\n.SH WIKI\n.I https://github.com/jarun/buku/wiki\n.SH REPORTING BUGS\n.I https://github.com/jarun/buku/issues\n.SH LICENSE\nCopyright \\(co 2015-2026 Arun Prakash Jana <engineerarun@gmail.com>.\n.PP\nLicense GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.\n.br\nThis is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.\n"
  },
  {
    "path": "buku.py",
    "content": "#!/usr/bin/env python3\n#\n# Bookmark management utility\n#\n# Copyright © 2015-2026 Arun Prakash Jana <engineerarun@gmail.com>\n#\n# This program is free software: you can redistribute it and/or modify\n# it under the terms of the GNU General Public License as published by\n# the Free Software Foundation, either version 3 of the License, or\n# (at your option) any later version.\n#\n# This program is distributed in the hope that it will be useful,\n# but WITHOUT ANY WARRANTY; without even the implied warranty of\n# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n# GNU General Public License for more details.\n#\n# You should have received a copy of the GNU General Public License\n# along with buku.  If not, see <http://www.gnu.org/licenses/>.\nfrom __future__ import annotations  # for |\n\nimport argparse\nimport calendar\nimport codecs\nimport collections\nimport contextlib\nimport email.message\nimport json\nimport locale\nimport logging\nimport os\nimport platform\nimport random\nimport re\nimport shutil\nimport signal\nimport sqlite3\nimport struct\nimport subprocess\nimport sys\nimport tempfile\nimport textwrap\nimport threading\nimport time\nimport unicodedata\nimport webbrowser\nfrom enum import Enum\nfrom itertools import chain\nfrom functools import total_ordering\nfrom subprocess import DEVNULL, PIPE, Popen\nfrom typing import Any, Dict, List, Optional, Tuple, NamedTuple, TypeAlias, TypeVar\nfrom collections.abc import Sequence, Set, Callable\nfrom warnings import warn\nimport xml.etree.ElementTree as ET\nfrom urllib.parse import urlparse  # urllib3.util.parse_url() encodes netloc\n\nimport urllib3\nfrom bs4 import BeautifulSoup\nfrom bs4.dammit import EncodingDetector\nfrom urllib3.util import Retry, make_headers\n\ntry:\n    from mypy_extensions import TypedDict\nexcept ImportError:\n    TypedDict = None  # type: ignore\n\n__version__ = '5.1'\n__author__ = 'Arun Prakash Jana <engineerarun@gmail.com>'\n__license__ = 'GPLv3'\n\n# Global variables\nINTERRUPTED = False  # Received SIGINT\nDELIM = ','  # Delimiter used to store tags in DB\nSKIP_MIMES = {'.pdf', '.txt'}\nPROMPTMSG = 'buku (? for help): '  # Prompt message string\n\nstrip_delim = lambda s, delim=DELIM, sub=' ': str(s).replace(delim, sub)\ntaglist = lambda ss: sorted(set(s.lower().strip() for s in ss if (s or '').strip()))\nparse_order = lambda order: [s for ss in order for s in re.split(r'\\s*,\\s*', ss.strip()) if s]\nlike_escape = lambda s, c='`': s.replace(c, c+c).replace('_', c+'_').replace('%', c+'%')\nsplit_by_marker = lambda s: re.split(r'\\s+(?=[.:>#*])', s)\n\ndef taglist_str(tag_str, convert=None):\n    tags = taglist(tag_str.split(DELIM))\n    return delim_wrap(DELIM.join(tags if not convert else taglist(convert(tags))))\n\ndef filter_from(values, subset, *, exclude=False):\n    subset, exclude = set(subset), bool(exclude)\n    return [x for x in values if (x in subset) != exclude]\n\n\n# Default format specifiers to print records\nID_STR = '%d. %s [%s]\\n'\nID_DB_STR = '%d. %s'\nMUTE_STR = '%s (L)\\n'\nURL_STR = '   > %s\\n'\nDESC_STR = '   + %s\\n'\nDESC_WRAP = '%s%s'\nTAG_STR = '   # %s\\n'\nTAG_WRAP = '%s%s'\n\n# Colormap for color output from \"googler\" project\nCOLORMAP = {k: '\\x1b[%sm' % v for k, v in {\n    'a': '30', 'b': '31', 'c': '32', 'd': '33',\n    'e': '34', 'f': '35', 'g': '36', 'h': '37',\n    'i': '90', 'j': '91', 'k': '92', 'l': '93',\n    'm': '94', 'n': '95', 'o': '96', 'p': '97',\n    'A': '30;1', 'B': '31;1', 'C': '32;1', 'D': '33;1',\n    'E': '34;1', 'F': '35;1', 'G': '36;1', 'H': '37;1',\n    'I': '90;1', 'J': '91;1', 'K': '92;1', 'L': '93;1',\n    'M': '94;1', 'N': '95;1', 'O': '96;1', 'P': '97;1',\n    'x': '0', 'X': '1', 'y': '7', 'Y': '7;1', 'z': '2',\n}.items()}\n\n# DB flagset values\n[FLAG_NONE, FLAG_IMMUTABLE] = [0x00, 0x01]\n\nFIELD_FILTER = {\n    1: ('id', 'url'),\n    2: ('id', 'url', 'tags'),\n    3: ('id', 'title'),\n    4: ('id', 'url', 'title', 'tags'),\n    5: ('id', 'title', 'tags'),\n    10: ('url',),\n    20: ('url', 'tags'),\n    30: ('title',),\n    40: ('url', 'title', 'tags'),\n    50: ('title', 'tags'),\n}\nALL_FIELDS = ('id', 'url', 'title', 'desc', 'tags')\nJSON_FIELDS = {'id': 'index', 'url': 'uri', 'desc': 'description'}\n\nUSER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64; rv:145.0) Gecko/20100101 Firefox/145.0'\nMYHEADERS = None  # Default dictionary of headers\nMYPROXY = None  # Default proxy\nTEXT_BROWSERS = ['elinks', 'links', 'links2', 'lynx', 'w3m', 'www-browser']\nIGNORE_FF_BOOKMARK_FOLDERS = frozenset([\"placesRoot\", \"bookmarksMenuFolder\"])\nPERMANENT_REDIRECTS = {301, 308}\n\nSCHEME_HTTP = 'http'\n\nIntSet: TypeAlias = Set[int] | range\nInts: TypeAlias = Sequence[int] | IntSet\nIntOrInts: TypeAlias = int | Ints\n_T = TypeVar('T')  # pylint: disable=typevar-name-mismatch\nValues: TypeAlias = Sequence[_T] | Set[_T]\n\n# Set up logging\nLOGGER = logging.getLogger()\nLOGDBG = LOGGER.debug\nLOGERR = LOGGER.error\n\n# Define the default path to ca-certificates\n# In Linux distros with openssl, it is /etc/ssl/certs/ca-certificates.crt\n# Fall back to use `certifi` otherwise\nif sys.platform.startswith('linux') and os.path.isfile('/etc/ssl/certs/ca-certificates.crt'):\n    CA_CERTS = '/etc/ssl/certs/ca-certificates.crt'\nelse:\n    import certifi\n    CA_CERTS = certifi.where()\n\n\nclass BukuCrypt:\n    \"\"\"Class to handle encryption and decryption of\n    the database file. Functionally a separate entity.\n\n    Involves late imports in the static functions but it\n    saves ~100ms each time. Given that encrypt/decrypt are\n    not done automatically and any one should be called at\n    a time, this doesn't seem to be an outrageous approach.\n    \"\"\"\n\n    # Crypto constants\n    BLOCKSIZE = 0x10000  # 64 KB blocks\n    SALT_SIZE = 0x20\n    CHUNKSIZE = 0x80000  # Read/write 512 KB chunks\n\n    @staticmethod\n    def get_filehash(filepath):\n        \"\"\"Get the SHA256 hash of a file.\n\n        Parameters\n        ----------\n        filepath : str\n            Path to the file.\n\n        Returns\n        -------\n        hash : bytes\n            Hash digest of file.\n        \"\"\"\n\n        from hashlib import sha256\n\n        with open(filepath, 'rb') as fp:\n            hasher = sha256()\n            buf = fp.read(BukuCrypt.BLOCKSIZE)\n            while len(buf) > 0:\n                hasher.update(buf)\n                buf = fp.read(BukuCrypt.BLOCKSIZE)\n\n            return hasher.digest()\n\n    @staticmethod\n    def encrypt_file(iterations=8, dbfile=None, encfile=None, password=None, replace=True):\n        \"\"\"Encrypt the bookmarks database file.\n\n        Parameters\n        ----------\n        iterations : int\n            Number of iterations for key generation. (Defaults to 8)\n        dbfile : str, optional\n            Custom database file path (including filename). Fallback value is the default DB.\n        encfile : str, optional\n            Encoded dbfile. (Defaults to dbfile + '.enc')\n        password : str, optional\n            Password to use (if not provided, will be prompted from the user).\n        replace : bool\n            If True (default), the original file will be removed on success.\n        \"\"\"\n        BukuCrypt(iterations, dbfile, encfile, password, replace)._encrypt_file()\n\n    @staticmethod\n    def decrypt_file(iterations=8, dbfile=None, encfile=None, password=None, replace=True):\n        \"\"\"Decrypt the bookmarks database file.\n\n        Parameters\n        ----------\n        iterations : int\n            Number of iterations for key generation. (Defaults to 8)\n        dbfile : str, optional\n            Custom database file path (including filename).\n            The '.enc' suffix must be omitted.\n        encfile : str, optional\n            Encoded dbfile. (Defaults to dbfile + '.enc')\n        password : str, optional\n            Password to use (if not provided, will be prompted from the user).\n        replace : bool\n            If True (default), the original file will be removed on success.\n        \"\"\"\n        BukuCrypt(iterations, dbfile, encfile, password, replace)._decrypt_file()\n\n    def __init__(self, iterations=8, dbfile=None, encfile=None, password=None, replace=True):\n        try:\n            from getpass import getpass\n            from hashlib import sha256\n\n            from cryptography.hazmat.backends import default_backend\n            from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes\n        except ImportError as e:\n            raise RuntimeError('cryptography lib(s) missing') from e\n        self._sha256, self._default_backend = sha256, default_backend\n        self._Cipher, self._algorithms, self._modes = Cipher, algorithms, modes\n        self._getpass = (getpass if sys.stdin.isatty() else (lambda: sys.stdin.readline().rstrip('\\n')))\n\n        if iterations < 1:\n            raise RuntimeError('Iterations must be >= 1')\n\n        self.iterations, self.password, self.replace = iterations, password, replace\n        self.dbfile = dbfile or os.path.join(BukuDb.get_default_dbdir(), 'bookmarks.db')\n        self.encfile = encfile or (self.dbfile + '.enc')\n\n        self._db_exists = os.path.exists(self.dbfile)\n        self._enc_exists = os.path.exists(self.encfile)\n        if self._db_exists and self._enc_exists:\n            raise RuntimeError('Both encrypted and flat DB files exist!')\n\n    def _encrypt_file(self):\n        if not self._db_exists:\n            raise RuntimeError(f'{self.dbfile} missing. Already encrypted?')\n\n        if not self.password:\n            self.password = self._getpass()\n            if not self.password:\n                raise RuntimeError('Empty password')\n            passconfirm = self._getpass()\n            if not passconfirm:\n                raise RuntimeError('Empty password')\n            if self.password != passconfirm:\n                raise RuntimeError('Passwords do not match')\n\n        try:\n            self._encrypt()\n            if self.replace:\n                os.remove(self.dbfile)\n        except Exception as e:\n            with contextlib.suppress(FileNotFoundError):\n                os.remove(self.encfile)\n            raise RuntimeError(e) from e\n\n    def _decrypt_file(self):\n        if not self._enc_exists:\n            raise RuntimeError(f'{self.encfile} missing')\n\n        self.password = self.password or self._getpass()\n        if not self.password:\n            raise RuntimeError('Empty password')\n\n        try:\n            enchash = self._decrypt()\n            # Match hash of generated file with that of original DB file\n            dbhash = BukuCrypt.get_filehash(self.dbfile)\n            if dbhash != enchash:\n                os.remove(self.dbfile)\n                raise RuntimeError('Decryption failed')\n            if self.replace:\n                os.remove(self.encfile)\n        except struct.error as e:\n            with contextlib.suppress(FileNotFoundError):\n                os.remove(self.dbfile)\n            raise RuntimeError('Tainted file') from e\n        except Exception as e:\n            with contextlib.suppress(FileNotFoundError):\n                os.remove(self.dbfile)\n            raise RuntimeError(e) from e\n\n    def _cipher(self, key, iv):\n        return self._Cipher(self._algorithms.AES(key), self._modes.CBC(iv), backend=self._default_backend())\n\n    def _key(self, salt):\n        key = ('%s%s' % (self.password, salt.decode('utf-8', 'replace'))).encode('utf-8')\n        for _ in range(self.iterations):\n            key = self._sha256(key).digest()\n        return key\n\n    def _encrypt(self):\n        # Get SHA256 hash of DB file\n        dbhash = BukuCrypt.get_filehash(self.dbfile)\n\n        # Generate random 256-bit salt and key\n        salt = os.urandom(BukuCrypt.SALT_SIZE)\n        iv = os.urandom(16)\n        encryptor = self._cipher(self._key(salt), iv).encryptor()\n        filesize = os.path.getsize(self.dbfile)\n\n        with open(self.dbfile, 'rb') as infp, open(self.encfile, 'wb') as outfp:\n            outfp.write(struct.pack('<Q', filesize))\n            outfp.write(salt)\n            outfp.write(iv)\n\n            # Embed DB file hash in encrypted file\n            outfp.write(dbhash)\n\n            while chunk := infp.read(BukuCrypt.CHUNKSIZE):\n                if len(chunk) % 16 != 0:\n                    chunk = b'%b%b' % (chunk, b' ' * (16 - len(chunk) % 16))\n                outfp.write(encryptor.update(chunk))\n\n            outfp.write(encryptor.finalize())\n        return dbhash\n\n    def _decrypt(self):\n        with open(self.encfile, 'rb') as infp:\n            size = struct.unpack('<Q', infp.read(struct.calcsize('Q')))[0]\n\n            # Read 256-bit salt and generate key\n            salt = infp.read(32)\n            iv = infp.read(16)\n            decryptor = self._cipher(self._key(salt), iv).decryptor()\n\n            # Get original DB file's SHA256 hash from encrypted file\n            enchash = infp.read(32)\n\n            with open(self.dbfile, 'wb') as outfp:\n                while chunk := infp.read(BukuCrypt.CHUNKSIZE):\n                    outfp.write(decryptor.update(chunk))\n                outfp.write(decryptor.finalize())\n                outfp.truncate(size)\n        return enchash\n\n\n@total_ordering\nclass SortKey:\n    def __init__(self, value, ascending=True):\n        self.value, self.ascending = value, bool(ascending)\n\n    def __eq__(self, other):\n        other = (other.value if isinstance(other, SortKey) else other)\n        return self.value == other\n\n    def __lt__(self, other):\n        other = (other.value if isinstance(other, SortKey) else other)\n        return self.value != other and ((self.value < other) == self.ascending)\n\n    def __repr__(self):\n        return ('+' if self.ascending else '-') + repr(self.value)\n\n\nclass FetchResult(NamedTuple):\n    url: str                            # resulting URL after following PERMANENT redirects\n    title: str = ''\n    desc: str = ''\n    keywords: str = ''\n    mime: bool = False\n    bad: bool = False\n    fetch_status: Optional[int] = None  # None means no fetch occurred (e.g. due to a network error)\n\n    def tag_redirect(self, pattern: str = None) -> str:\n        return ('' if self.fetch_status not in PERMANENT_REDIRECTS else (pattern or 'http:{}').format(self.fetch_status))\n\n    def tag_error(self, pattern: str = None) -> str:\n        return ('' if (self.fetch_status or 0) < 400 else (pattern or 'http:{}').format(self.fetch_status))\n\n    def tags(self, *, keywords: bool = True, redirect: bool | str = False, error: bool | str = False) -> str:\n        _redirect = redirect and self.tag_redirect(None if redirect is True else redirect)\n        _error = error and self.tag_error(None if error is True else error)\n        return DELIM.join(taglist((keywords and self.keywords or '').split(DELIM) + [_redirect, _error]))\n\n\nclass BookmarkVar(NamedTuple):\n    \"\"\"Bookmark data named tuple\"\"\"\n    id: int\n    url: str\n    title: Optional[str] = None\n    tags_raw: str = ''\n    desc: str = ''\n    flags: int = FLAG_NONE\n\n    @property\n    def immutable(self) -> bool:\n        return bool(self.flags & FLAG_IMMUTABLE)\n\n    @property\n    def tags(self) -> str:\n        return self.tags_raw[1:-1]\n\n    @property\n    def taglist(self) -> List[str]:\n        return [x for x in self.tags_raw.split(',') if x]\n\n    @property\n    def netloc(self) -> str:\n        return get_netloc(self.url) or ''\n\nbookmark_vars = lambda xs: ((x if isinstance(x, BookmarkVar) else BookmarkVar(*x)) for x in xs)\n\n\nclass BukuDb:\n    \"\"\"Abstracts all database operations.\n\n    Attributes\n    ----------\n    conn : sqlite database connection.\n    cur : sqlite database cursor.\n    json : string\n        Empty string if results should be printed in JSON format to stdout.\n        Nonempty string if results should be printed in JSON format to file. The string has to be a valid path.\n        None if the results should be printed as human-readable plaintext.\n    field_filter : int\n        Indicates format for displaying bookmarks. Default is 0.\n    chatty : bool\n        Sets the verbosity of the APIs. Default is False.\n    \"\"\"\n\n    def __init__(\n            self, json: Optional[str] = None, field_filter: int = 0, chatty: bool = False,\n            dbfile: Optional[str] = None, colorize: bool = True, default_scheme: str = SCHEME_HTTP) -> None:\n        \"\"\"Database initialization API.\n\n        Parameters\n        ----------\n        json : string\n            Empty string if results should be printed in JSON format to stdout.\n            Nonempty string if results should be printed in JSON format to file. The string has to be a valid path.\n            None if the results should be printed as human-readable plaintext.\n        field_filter : int\n            Indicates format for displaying bookmarks. Default is 0.\n        chatty : bool\n            Sets the verbosity of the APIs. Default is False.\n        colorize : bool\n            Indicates whether color should be used in output. Default is True.\n        default_scheme : str\n            Scheme to assume if missing from bookmark's URI. Default is http.\n        \"\"\"\n\n        self.json = json\n        self.field_filter = field_filter\n        self.chatty = chatty\n        self.colorize = colorize\n        self.conn, self.cur = BukuDb.initdb(dbfile, self.chatty)\n        self.lock = threading.RLock()  # repeatable lock, only blocks *concurrent* access\n        self._to_export = None  # type: Optional[Dict[str, str | BookmarkVar]]\n        self._to_delete = None  # type: Optional[int | Sequence[int] | Set[int] | range]\n        self.default_scheme = default_scheme\n\n    @staticmethod\n    def get_default_dbdir():\n        \"\"\"Determine the directory path where dbfile will be stored.\n\n        If $BUKU_DEFAULT_DBDIR is specified, use it\n        else if $XDG_DATA_HOME is defined, use $XDG_DATA_HOME/buku\n        else if $HOME exists, use $HOME/.local/share/buku\n        else if the platform is Windows and %APPDATA% exists, use %APPDATA%\\\\buku\n        else use the current directory.\n\n        Returns\n        -------\n        str\n            Path to database file.\n        \"\"\"\n\n        _get = os.environ.get\n        if _get('BUKU_DEFAULT_DBDIR'):\n            return os.path.abspath(_get('BUKU_DEFAULT_DBDIR'))\n        home_locations = [\n            _get('XDG_DATA_HOME'),\n            _get('HOME') and os.path.join(_get('HOME'), '.local', 'share'),\n            sys.platform == 'win32' and _get('APPDATA'),\n        ]\n        data_home = next((s for s in home_locations if s), None)\n        return (os.path.join(data_home, 'buku') if data_home else os.getcwd())\n\n    @staticmethod\n    def initdb(dbfile: Optional[str] = None, chatty: bool = False) -> Tuple[sqlite3.Connection, sqlite3.Cursor]:\n        \"\"\"Initialize the database connection.\n\n        Create DB file and/or bookmarks table if they don't exist.\n        Alert on encryption options on first execution.\n\n        Parameters\n        ----------\n        dbfile : str, optional\n            Custom database file path (including filename).\n        chatty : bool\n            If True, shows informative message on DB creation.\n\n        Returns\n        -------\n        tuple\n            (connection, cursor).\n        \"\"\"\n\n        if not dbfile:\n            dbpath = BukuDb.get_default_dbdir()\n            filename = 'bookmarks.db'\n            dbfile = os.path.join(dbpath, filename)\n        else:\n            dbfile = os.path.abspath(dbfile)\n            dbpath, filename = os.path.split(dbfile)\n\n        try:\n            if not os.path.exists(dbpath):\n                os.makedirs(dbpath)\n        except Exception as e:\n            LOGERR(e)\n            os._exit(1)\n\n        db_exists = os.path.exists(dbfile)\n        enc_exists = os.path.exists(dbfile + '.enc')\n\n        if db_exists and not enc_exists:\n            pass\n        elif enc_exists and not db_exists:\n            LOGERR('Unlock database first')\n            sys.exit(1)\n        elif db_exists and enc_exists:\n            LOGERR('Both encrypted and flat DB files exist!')\n            sys.exit(1)\n        elif chatty:\n            # not db_exists and not enc_exists\n            print('DB file is being created at %s.\\nYou should encrypt it.' % dbfile)\n\n        try:\n            # Create a connection\n            conn = sqlite3.connect(dbfile, check_same_thread=False)\n            conn.create_function('REGEXP', 2, regexp)\n            conn.create_function('NETLOC', 1, get_netloc)\n            cur = conn.cursor()\n\n            # Create table if it doesn't exist\n            # flags: designed to be extended in future using bitwise masks\n            # Masks:\n            #     0b00000001: set title immutable\n            cur.execute('CREATE TABLE if not exists bookmarks ('\n                        'id integer PRIMARY KEY, '\n                        'URL text NOT NULL UNIQUE, '\n                        'metadata text default \\'\\', '\n                        'tags text default \\',\\', '\n                        'desc text default \\'\\', '\n                        'flags integer default 0)')\n            conn.commit()\n        except Exception as e:\n            LOGERR('initdb(): %s', e)\n            raise e\n\n        return (conn, cur)\n\n    @property\n    def dbfile(self) -> str:\n        return next(path for _, name, path in self.conn.execute('PRAGMA database_list') if name == 'main')\n\n    @property\n    def dbname(self) -> str:\n        return os.path.basename(self.dbfile).removesuffix('.db')\n\n    def _fetch(self, query: str, *args, lock: bool = True) -> List[BookmarkVar]:\n        if not lock:\n            self.cur.execute(query, args)\n            return [BookmarkVar(*x) for x in self.cur.fetchall()]\n        with self.lock:\n            return self._fetch(query, *args, lock=False)\n\n    def _fetch_first(self, query: str, *args, lock: bool = True) -> Optional[BookmarkVar]:\n        rows = self._fetch(query + ' LIMIT 1', *args, lock=lock)\n        return rows[0] if rows else None\n\n    def _ordering(self, fields=['+id'], for_db=True) -> List[Tuple[str, bool]]:\n        \"\"\"Converts field list to ordering parameters (for DB query or entity list sorting).\n        Fields are listed in priority order, with '+'/'-' prefix signifying ASC/DESC; assuming ASC if not specified.\n        Other than names from DB, you can pass those from JSON export.\"\"\"\n        _field = lambda s: re.sub(r'^[+-]?(#)? *', r'\\1', s).rstrip().lower()\n        tags = {_field(s) for s in (fields or []) if re.fullmatch(r'[+-]?#[^,]+', s)}\n        names = {'index': 'id', 'uri': 'url', 'description': 'desc', **({'title': 'metadata'} if for_db else {'metadata': 'title'})}\n        valid = list(names) + list(names.values()) + ['tags', 'netloc'] + list(tags)\n        _fields = [(_field(s), not s.startswith('-')) for s in (fields or [])]\n        _fields = [(names.get(field, field), direction) for field, direction in _fields if field in valid]\n        return _fields or [('id', True)]\n\n    def _sort(self, records: List[BookmarkVar], fields=['+id'], ignore_case=True) -> List[BookmarkVar]:\n        text_fields = (set() if not ignore_case else {'url', 'desc', 'title', 'tags', 'netloc'})\n        get = lambda x, k: (k[1:] in x.taglist if k.startswith('#') else\n                            getattr(x, k) if k not in text_fields else str(getattr(x, k) or '').lower())\n        order = self._ordering(fields, for_db=False)\n        return sorted(bookmark_vars(records), key=lambda x: [SortKey(get(x, k), ascending=asc) for k, asc in order])\n\n    def _order(self, fields=['+id'], ignore_case=True) -> str:\n        \"\"\"Converts field list to SQL 'ORDER BY' parameters. (See also BukuDb._ordering().)\"\"\"\n        text_fields = (set() if not ignore_case else {'url', 'desc', 'metadata', 'tags'})\n        get = lambda field: (\"tags LIKE '%,{0},%'\".format(field[1:].replace(\"'\", \"''\")) if field.startswith('#') else\n                             'LOWER(NETLOC(url))' if field == 'netloc' else field if field not in text_fields else f'LOWER({field})')\n        return ', '.join(f'{get(field)} {\"ASC\" if direction else \"DESC\"}' for field, direction in self._ordering(fields))\n\n    def get_rec_all(self, *, lock: bool = True, order: List[str] = ['id'], ignore_case: bool = True):\n        \"\"\"Get all the bookmarks in the database.\n\n        Parameters\n        ----------\n        lock : bool\n            Whether to restrict concurrent access (True by default).\n        order : list of str\n            Order description (fields from JSON export or DB, prepended with '+'/'-' for ASC/DESC).\n        ignore_case : bool\n            Whether to ignore case when applying order (True by default).\n\n        Returns\n        -------\n        list\n            A list of tuples representing bookmark records.\n        \"\"\"\n\n        return self._fetch(f'SELECT * FROM bookmarks ORDER BY {self._order(order, ignore_case)}', lock=lock)\n\n    def get_rec_by_id(self, index: int, *, lock: bool = True) -> Optional[BookmarkVar]:\n        \"\"\"Get a bookmark from database by its ID.\n\n        Parameters\n        ----------\n        index : int\n            DB index of bookmark record.\n        lock : bool\n            Whether to restrict concurrent access (True by default).\n\n        Returns\n        -------\n        BookmarkVar or None\n            Bookmark data, or None if index is not found.\n        \"\"\"\n\n        return self._fetch_first('SELECT * FROM bookmarks WHERE id = ?', index, lock=lock)\n\n    def get_rec_all_by_ids(self, indices: Ints, *, lock: bool = True, order: List[str] = ['id']):\n        \"\"\"Get all the bookmarks in the database.\n\n        Parameters\n        ----------\n        indices : int[] | int{} | range\n            DB indices of bookmark records.\n        lock : bool\n            Whether to restrict concurrent access (True by default).\n        order : list of str\n            Order description (fields from JSON export or DB, prepended with '+'/'-' for ASC/DESC).\n\n        Returns\n        -------\n        list\n            A list of tuples representing bookmark records.\n        \"\"\"\n\n        _order, placeholder = self._order(order), ', '.join(['?'] * len(indices))\n        return indices and self._fetch(f'SELECT * FROM bookmarks WHERE id IN ({placeholder}) ORDER BY {_order}',\n                                       *list(indices), lock=lock)\n\n    def get_rec_id(self, url: str, *, lock: bool = True):\n        \"\"\"Check if URL already exists in DB.\n\n        Parameters\n        ----------\n        url : str\n            A URL to search for in the DB.\n        lock : bool\n            Whether to restrict concurrent access (True by default).\n\n        Returns\n        -------\n        int\n            DB index, or None if URL not found in DB.\n        \"\"\"\n\n        row = self._fetch_first('SELECT * FROM bookmarks WHERE url = ?', url, lock=lock)\n        return row and row.id\n\n    def get_rec_ids(self, urls: Values[str], *, lock: bool = True):\n        \"\"\"Check if URL already exists in DB.\n\n        Parameters\n        ----------\n        urls : str[] | str{}\n            URLs to search for in the DB.\n        lock : bool\n            Whether to restrict concurrent access (True by default).\n\n        Returns\n        -------\n        list\n            A list of DB indices.\n        \"\"\"\n\n        if not urls:\n            return []\n        if not lock:\n            placeholder = ', '.join(['?'] * len(urls))\n            self.cur.execute(f'SELECT id FROM bookmarks WHERE url IN ({placeholder})', list(urls))\n            return [x[0] for x in self.cur.fetchall()]\n        with self.lock:\n            return self.get_rec_ids(urls, lock=False)\n\n    def get_max_id(self, *, lock: bool = True) -> int:\n        \"\"\"Fetch the ID of the last record.\n\n        Parameters\n        ----------\n        lock : bool\n            Whether to restrict concurrent access (True by default).\n\n        Returns\n        -------\n        int\n            ID of the record if any record exists, else None.\n        \"\"\"\n\n        if not lock:\n            self.cur.execute('SELECT MAX(id) FROM bookmarks')\n            return self.cur.fetchall()[0][0]\n        with self.lock:\n            return self.get_max_id(lock=False)\n\n    def reorder(self, order: List[str], *, ignore_case=True):\n        \"\"\"Change indices of all records in DB to match the specified order.\n\n        Parameters\n        ----------\n        order : list of str\n            Order description (fields from JSON export or DB, prepended with '+'/'-' for ASC/DESC).\n        ignore_case : bool\n            Whether to ignore case when applying order (True by default).\n        \"\"\"\n        with self.lock:\n            sorted_urls = [x.url for x in self.get_rec_all(lock=False, order=order, ignore_case=ignore_case)]\n            self.cur.execute('UPDATE bookmarks SET id = -id')\n            for idx, url in enumerate(sorted_urls, start=1):\n                self.cur.execute('UPDATE bookmarks SET id = ? WHERE url = ?', (idx, url))\n            self.conn.commit()\n            self.cur.execute('VACUUM')\n\n    def add_rec(\n            self,\n            url: str,\n            title_in: Optional[str] = None,\n            tags_in: Optional[str] = None,\n            desc: Optional[str] = None,\n            immutable: bool = False,\n            delay_commit: bool = False,\n            fetch: bool = True,\n            url_redirect: bool = False,\n            tag_redirect: bool | str = False,\n            tag_error: bool | str = False,\n            del_error: Optional[IntSet] = None,\n            tags_fetch: bool = True,\n            tags_except: Optional[str] = None) -> int:\n        \"\"\"Add a new bookmark.\n\n        Parameters\n        ----------\n        url : str\n            URL to bookmark.\n        title_in : str, optional\n            Title to add manually. Default is None.\n        tags_in : str, optional\n            Comma-separated tags to add manually, instead of fetching them. Default is None.\n        tags_except : str, optional\n            These are removed from the resulting tags list. Default is None.\n        tags_fetch : bool\n            True if tags parsed from the fetched page should be included. Default is True.\n        desc : str, optional\n            Description of the bookmark. Default is None.\n        immutable : bool\n            Indicates whether to disable title fetch from web. Default is False.\n        delay_commit : bool\n            True if record should not be committed to the DB,\n            leaving commit responsibility to caller. Default is False.\n        fetch : bool\n            Fetch page from web and parse for data. Required fetch-status params to take effect.\n        url_redirect : bool\n            Bookmark the URL produced after following all PERMANENT redirects.\n        tag_redirect : bool | str\n            Adds a tag by the given pattern if the url resolved to a PERMANENT\n            redirect. (True means the default pattern 'http:{}'.)\n        tag_error : bool | str\n            Adds a tag by the given pattern if the url resolved to a HTTP error.\n            (True means the default pattern 'http:{}'.)\n        del_error : int{} | range, optional\n            Do not add the bookmark if HTTP response status is in the given set or range.\n            Also prevents the bookmark from being added on a network error.\n\n        Returns\n        -------\n        int\n            DB index of new bookmark on success, None on failure.\n        \"\"\"\n\n        # Return error for empty URL\n        if not url:\n            LOGERR('Invalid URL')\n            return None\n\n        # Ensure that the URL does not exist in DB already\n        id = self.get_rec_id(url)\n        if id:\n            LOGERR('URL [%s] already exists at index %d', url, id)\n            return None\n\n        if fetch:\n            # Fetch data\n            result = fetch_data(url)\n            if result.bad:\n                print('Malformed URL\\n')\n            elif result.mime:\n                LOGDBG('HTTP HEAD requested')\n            elif not result.title and title_in is None:\n                print('No title\\n')\n            else:\n                LOGDBG('Title: [%s]', result.title)\n        else:\n            result = FetchResult(url, fetch_status=200)\n            LOGDBG('ptags: [%s]', result.tags(redirect=tag_redirect, error=tag_error))\n\n        url = (result.url if url_redirect else url)\n        title = (title_in if title_in is not None else result.title)\n\n        # Fix up tags, if broken\n        tags_exclude = set(taglist((tags_except or '').split(DELIM)))\n        tags_fetched = result.tags(keywords=tags_fetch, redirect=tag_redirect, error=tag_error)\n        tags = taglist_str((tags_in or '') + DELIM + tags_fetched,\n                           lambda ss: [s for s in ss if s not in tags_exclude])\n        LOGDBG('tags: [%s]', tags)\n\n        # Process description\n        desc = (desc if desc is not None else result.desc) or ''\n\n        try:\n            assert not del_error or result.fetch_status is not None, 'Network error'\n            assert not del_error or result.fetch_status not in del_error, f'HTTP error {result.fetch_status}'\n            flagset = FLAG_NONE\n            if immutable:\n                flagset |= FLAG_IMMUTABLE\n\n            qry = 'INSERT INTO bookmarks(URL, metadata, tags, desc, flags) VALUES (?, ?, ?, ?, ?)'\n            with self.lock:\n                self.cur.execute(qry, (url, title, tags, desc, flagset))\n                if not delay_commit:\n                    self.conn.commit()\n                if self.chatty:\n                    self.print_rec(self.cur.lastrowid)\n                return self.cur.lastrowid\n        except Exception as e:\n            LOGERR('add_rec(): %s', e)\n            return None\n\n    def append_tag_at_index(self, index, tags_in, delay_commit=False):\n        \"\"\"Append tags to bookmark tagset at index.\n\n        Parameters\n        ----------\n        index : int | int[] | int{} | range, optional\n            DB index of the record. 0 or empty indicates all records.\n        tags_in : str\n            Comma-separated tags to add manually.\n        delay_commit : bool\n            True if record should not be committed to the DB,\n            leaving commit responsibility to caller. Default is False.\n\n        Returns\n        -------\n        bool\n            True on success, False on failure.\n        \"\"\"\n\n        if tags_in is None or tags_in == DELIM:\n            return True\n        indices = (None if not index else [index] if isinstance(index, int) else index)\n\n        with self.lock:\n            if not indices:\n                resp = read_in('Append the tags to ALL bookmarks? (y/n): ')\n                if resp != 'y':\n                    return False\n\n                self.cur.execute('SELECT id, tags FROM bookmarks ORDER BY id ASC')\n            else:\n                placeholder = ', '.join(['?'] * len(indices))\n                self.cur.execute(f'SELECT id, tags FROM bookmarks WHERE id IN ({placeholder}) ORDER BY id ASC', tuple(indices))\n\n            resultset = self.cur.fetchall()\n            if resultset:\n                query = 'UPDATE bookmarks SET tags = ? WHERE id = ?'\n                for row in resultset:\n                    tags = row[1] + tags_in[1:]\n                    tags = parse_tags([tags])\n                    self.cur.execute(query, (tags, row[0],))\n                    if self.chatty and not delay_commit:\n                        self.print_rec(row[0])\n            else:\n                return False\n\n            if not delay_commit:\n                self.conn.commit()\n\n        return True\n\n    def delete_tag_at_index(self, index, tags_in, delay_commit=False, chatty=True):\n        \"\"\"Delete tags from bookmark tagset at index.\n\n        Parameters\n        ----------\n        index : int | int[] | int{} | range, optional\n            DB index of bookmark record. 0 or empty indicates all records.\n        tags_in : str\n            Comma-separated tags to delete manually.\n        delay_commit : bool\n            True if record should not be committed to the DB,\n            leaving commit responsibility to caller. Default is False.\n        chatty: bool\n            Skip confirmation when set to False.\n\n        Returns\n        -------\n        bool\n            True on success, False on failure.\n        \"\"\"\n\n        if tags_in is None or tags_in == DELIM:\n            return True\n\n        tags_to_delete = tags_in.strip(DELIM).split(DELIM)\n        indices = (None if not index else [index] if isinstance(index, int) else index)\n\n        if len(indices or []) != 1:\n            if not indices and chatty:\n                resp = read_in('Delete the tag(s) from ALL bookmarks? (y/n): ')\n                if resp != 'y':\n                    return False\n\n            query = \"UPDATE bookmarks SET tags = replace(tags, ?, ?) WHERE tags LIKE ? ESCAPE '`'\"\n            if indices:\n                query += ' AND id IN ({})'.format(', '.join(['?'] * len(indices)))\n\n            count = 0\n            with self.lock:\n                for tag in tags_to_delete:\n                    tag = delim_wrap(tag)\n                    args = (tag, DELIM, '%'+like_escape(tag, '`')+'%') + tuple(indices or [])\n                    self.cur.execute(query, args)\n                    count += self.cur.rowcount\n\n                if count > 0 and not delay_commit:\n                    self.conn.commit()\n                    if self.chatty:\n                        print('%d record(s) updated' % count)\n\n            return True\n\n        # Process a single index\n        # Use SELECT and UPDATE to handle multiple tags at once\n        with self.lock:\n            query = 'SELECT id, tags FROM bookmarks WHERE id = ? LIMIT 1'\n            self.cur.execute(query, list(indices))\n            resultset = self.cur.fetchall()\n            if not resultset:\n                return False\n\n            query = 'UPDATE bookmarks SET tags = ? WHERE id = ?'\n            for row in resultset:\n                tags = row[1]\n\n                for tag in tags_to_delete:\n                    tags = tags.replace(delim_wrap(tag), DELIM)\n\n                self.cur.execute(query, (parse_tags([tags]), row[0],))\n                if self.chatty and not delay_commit:\n                    self.print_rec(row[0])\n\n                if not delay_commit:\n                    self.conn.commit()\n\n        return True\n\n    def update_rec(\n            self,\n            index: Optional[IntOrInts],\n            url: Optional[str] = None,\n            title_in: Optional[str] = None,\n            tags_in: Optional[str] = None,\n            desc: Optional[str] = None,\n            immutable: Optional[bool] = None,\n            threads: int = 4,\n            url_redirect: bool = False,\n            tag_redirect: bool | str = False,\n            tag_error: bool | str = False,\n            del_error: Optional[IntSet] = None,\n            export_on: Optional[IntSet] = None,\n            retain_order: bool = False) -> bool:\n        \"\"\"Update an existing record at (each) index.\n\n        Update all records if index is 0 or empty, and url is not specified.\n        URL is an exception because URLs are unique in DB.\n\n        Parameters\n        ----------\n        index : int | int[] | int{} | range, optional\n            DB index(es) of record(s). 0 or empty value indicates all records.\n        url : str, optional\n            Bookmark address.\n        title_in : str, optional\n            Title to add manually.\n        tags_in : str, optional\n            Comma-separated tags to add manually. Must start and end with comma.\n            Prefix with '+,' to append to current tags.\n            Prefix with '-,' to delete from current tags.\n        desc : str, optional\n            Description of bookmark.\n        immutable : bool, optional\n            Disable title fetch from web if True. Default is None (no change).\n        threads : int\n            Number of threads to use to refresh full DB. Default is 4.\n        url_redirect : bool\n            Update the URL to one produced after following all PERMANENT redirects.\n            (This could fail if the new URL is bookmarked already.)\n        tag_redirect : bool | str\n            Adds a tag by the given pattern if the url resolved to a PERMANENT\n            redirect. (True means the default pattern 'http:{}'.)\n        tag_error : bool | str\n            Adds a tag by the given pattern if the url resolved to a HTTP error.\n            (True means the default pattern 'http:{}'.)\n        del_error : int{} | range, optional\n            Delete the bookmark if HTTP response status is in the given set or range.\n            Does NOT cause deletion of the bookmark on a network error.\n        export_on : int{} | range, optional\n            Limit the export to URLs returning one of given HTTP codes; store old URLs.\n        retain_order : bool\n            If True, bookmark deletion will not result in their order being changed\n            (multiple indices will be updated instead).\n\n        Returns\n        -------\n        bool\n            True on success, False on failure. (Deletion by del_error counts as success.)\n        \"\"\"\n\n        arguments = []  # type: List[Any]\n        query = 'UPDATE bookmarks SET'\n        tag_modified = False\n        ret = True\n        indices = (None if not index else [index] if isinstance(index, int) else index)\n        index = indices and list(indices or [])[0]\n        single = len(indices or []) == 1\n        export_on, self._to_export = (export_on or set()), ({} if export_on else None)\n        tags_in = (tags_in or None if not tags_in or re.match('[+-],', tags_in) else delim_wrap(tags_in))\n\n        if url and not single:\n            LOGERR('All URLs cannot be same')\n            return False\n\n        if tags_in in ('+,', '-,'):\n            LOGERR('Please specify a tag')\n            return False\n\n        if indices and min(indices) > (self.get_max_id() or 0):  # none of the indices exist in DB?\n            return False\n\n        # Update description if passed as an argument\n        if desc is not None:\n            query += ' desc = ?,'\n            arguments += (desc,)\n\n        # Update immutable flag if passed as argument\n        if immutable is not None:\n            if immutable:\n                query += ' flags = flags | ?,'\n                arguments += (FLAG_IMMUTABLE,)\n            else:\n                query += ' flags = flags & ?,'\n                arguments += (~FLAG_IMMUTABLE,)\n\n        # Update title\n        #\n        # 1. If --title has no arguments, delete existing title\n        # 2. If --title has arguments, update existing title\n        # 3. If --title option is omitted at cmdline:\n        #    If URL is passed, update the title from web using the URL\n        # 4. If no other argument (url, tag, comment, immutable) passed,\n        #    update title from web using DB URL (if title is mutable)\n        fetch_title = {url, title_in, tags_in, desc, immutable} == {None}\n        network_test = url_redirect or tag_redirect or tag_error or del_error or export_on or fetch_title\n        if url and title_in is None:\n            network_test = False\n            _url = url or self.get_rec_by_id(index).url\n            result = fetch_data(_url)\n            if result.bad:\n                print('Malformed URL')\n            elif result.mime:\n                LOGDBG('HTTP HEAD requested')\n            elif not result.title:\n                print('No title')\n            else:\n                LOGDBG('Title: [%s]', result.title)\n\n            if result.desc and not desc:\n                query += ' desc = ?,'\n                arguments += (result.desc,)\n\n            if url_redirect and result.url != _url:\n                url = result.url\n\n            if result.fetch_status in export_on:  # storing the old URL\n                self._to_export[url or _url] = _url\n        else:\n            result = FetchResult(url, title_in)\n\n        if result.title is not None:\n            query += ' metadata = ?,'\n            arguments += (result.title,)\n\n        # Update URL if passed as argument\n        if url:\n            query += ' URL = ?,'\n            arguments += (url,)\n\n        if result.fetch_status in (del_error or []):\n            if result.fetch_status in export_on:  # storing the old record\n                self._to_export[url] = self.get_rec_by_id(index)\n            LOGERR('HTTP error %s', result.fetch_status)\n            return self.delete_rec(index, retain_order=retain_order)\n\n        if not indices and (arguments or tags_in):\n            resp = read_in('Update ALL bookmarks? (y/n): ')\n            if resp != 'y':\n                return False\n\n        if network_test:  # doing this before updates to backup records to-be-deleted in their original state\n            custom_tags = (tags_in if (tags_in or '').startswith(DELIM) else None)\n            ret = ret and self.refreshdb(indices, threads, url_redirect=url_redirect, tag_redirect=tag_redirect,\n                                         tag_error=tag_error, del_error=del_error, export_on=export_on,\n                                         update_title=fetch_title, custom_url=url, custom_tags=custom_tags, delay_delete=True)\n\n        # Update tags if passed as argument\n        _tags = result.tags(keywords=False, redirect=tag_redirect, error=tag_error)\n        if tags_in or _tags:\n            if not tags_in or tags_in.startswith('+,'):\n                tags = taglist_str((tags_in or '')[1:] + _tags)\n                chatty = self.chatty\n                self.chatty = False\n                ret = self.append_tag_at_index(indices, tags)\n                self.chatty = chatty\n                tag_modified = True\n            elif tags_in.startswith('-,'):\n                chatty = self.chatty\n                self.chatty = False\n                ret = self.delete_tag_at_index(indices, tags_in[1:])\n                if _tags:\n                    self.append_tag_at_index(indices, _tags)\n                self.chatty = chatty\n                tag_modified = True\n            elif not network_test:  # rely on custom_tags to avoid overwriting fetch-status tags\n                query += ' tags = ?,'\n                arguments += (taglist_str(tags_in + _tags),)\n\n        if not arguments:  # no arguments => nothing to update\n            if (tag_modified or network_test) and self.chatty:\n                self.print_rec(indices)\n            self.commit_delete(retain_order=retain_order)\n            return ret\n\n        query = query[:-1]\n        if indices:  # Only specified indices\n            query += ' WHERE id IN ({})'.format(', '.join(['?'] * len(indices)))\n            arguments += tuple(indices)\n\n        LOGDBG('update_rec query: \"%s\", args: %s', query, arguments)\n\n        with self.lock:\n            try:\n                self.cur.execute(query, arguments)\n                self.conn.commit()\n                if self.cur.rowcount > 0 and self.chatty:\n                    self.print_rec(index)\n                elif self.cur.rowcount == 0:\n                    if single:\n                        LOGERR('No matching index %d', index)\n                    else:\n                        LOGERR('No matches found')\n                    return False\n            except sqlite3.IntegrityError:\n                LOGERR('URL already exists')\n                return False\n            except sqlite3.OperationalError as e:\n                LOGERR(e)\n                return False\n            finally:\n                self.commit_delete(retain_order=retain_order)\n\n        return True\n\n    def refreshdb(\n            self,\n            index: Optional[IntOrInts],\n            threads: int,\n            url_redirect: bool = False,\n            tag_redirect: bool | str = False,\n            tag_error: bool | str = False,\n            del_error: Optional[IntSet] = None,\n            export_on: Optional[IntSet] = None,\n            update_title: bool = True,\n            custom_url: Optional[str] = None,\n            custom_tags: Optional[str] = None,\n            delay_delete: bool = False,\n            retain_order: bool = False) -> bool:\n        \"\"\"Refresh ALL (or specified) records in the database.\n\n        Fetch title for each bookmark from the web and update the records.\n        Doesn't update the title if fetched title is empty.\n\n        Notes\n        -----\n            This API doesn't change DB index, URL or tags of a bookmark.\n            (Unless one or more fetch-status parameters are supplied.)\n            This API is verbose.\n\n        Parameters\n        ----------\n        index : int | int[] | int{} | range, optional\n            DB index(es) of record(s) to update. 0 or empty value indicates all records.\n        threads: int\n            Number of threads to use to refresh full DB. Default is 4.\n        url_redirect : bool\n            Update the URL to one produced after following all PERMANENT redirects.\n            (This could fail if the new URL is bookmarked already.)\n        tag_redirect : bool | str\n            Adds a tag by the given pattern if the url resolved to a PERMANENT\n            redirect. (True means the default pattern 'http:{}'.)\n        tag_error : bool | str\n            Adds a tag by the given pattern if the url resolved to a HTTP error.\n            (True means the default pattern 'http:{}'.)\n        del_error : int{} | range, optional\n            Delete the bookmark if HTTP response status is in the given set or range.\n        export_on : int{} | range, optional\n            Limit the export to URLs returning one of given HTTP codes; store old URLs.\n        update_title : bool\n            Update titles/descriptions. (Can be turned off for network testing.)\n        custom_url : str, optional\n            Override URL to fetch. (Use for network testing of a single record before updating it.)\n        custom_tags : str, optional\n            Overwrite all tags. (Use to combine network testing with tags overwriting.)\n        delay_delete : bool\n            Delay scheduled deletions by del_error. (Use for network testing during update.)\n        retain_order : bool\n            If True, bookmark deletion will not result in their order being changed\n            (multiple indices will be updated instead).\n\n        Returns\n        -------\n        bool\n            True on success, False on failure. (Deletion by del_error counts as success.)\n        \"\"\"\n\n        indices = (None if not index else [index] if isinstance(index, int) else index)\n        index = indices and list(indices)[0]\n        export_on, self._to_export = (export_on or set()), ({} if export_on else None)\n        self._to_delete = []\n\n        if not update_title and not (url_redirect or tag_redirect or tag_error or del_error or export_on):\n            LOGERR('Noop update request')\n            return False\n        if custom_url and len(indices or []) != 1:\n            LOGERR('custom_url is only supported for a singular index')\n            return False\n\n        with self.lock:\n            if not indices:\n                self.cur.execute('SELECT id, url, tags, flags FROM bookmarks ORDER BY id ASC')\n            else:\n                placeholder = ', '.join(['?'] * len(indices))\n                self.cur.execute(f'SELECT id, url, tags, flags FROM bookmarks WHERE id IN ({placeholder}) ORDER BY id ASC',\n                                 tuple(indices))\n\n            resultset = self.cur.fetchall()\n            recs = len(resultset)\n            if not recs:\n                LOGERR('No matching index or title immutable or empty DB')\n                return False\n\n        # Set up strings to be printed\n        if self.colorize:\n            bad_url_str = '\\x1b[1mIndex %d: Malformed URL\\x1b[0m\\n'\n            mime_str = '\\x1b[1mIndex %d: HTTP HEAD requested\\x1b[0m\\n'\n            blank_url_str = '\\x1b[1mIndex %d: No title\\x1b[0m\\n'\n            success_str = 'Title: [%s]\\n\\x1b[92mIndex %d: updated\\x1b[0m\\n'\n        else:\n            bad_url_str = 'Index %d: Malformed URL\\n'\n            mime_str = 'Index %d: HTTP HEAD requested\\n'\n            blank_url_str = 'Index %d: No title\\n'\n            success_str = 'Title: [%s]\\nIndex %d: updated\\n'\n\n        done = {'value': 0}  # count threads completed\n        processed = {'value': 0}  # count number of records processed\n\n        # An additional call to generate default headers\n        # gen_headers() is called within fetch_data()\n        # However, this initial call to setup headers\n        # ensures there is no race condition among the\n        # initial threads to setup headers\n        if not MYHEADERS:\n            gen_headers()\n\n        def refresh(thread_idx, cond):\n            \"\"\"Inner function to fetch titles and update records.\n\n            Parameters\n            ----------\n            thread_idx : int\n                Thread index/ID.\n            cond : threading condition object.\n            \"\"\"\n\n            _count = 0\n\n            while True:\n                query = 'UPDATE bookmarks SET'\n                arguments = []\n\n                with cond:\n                    if resultset:\n                        id, url, tags, flags = resultset.pop()\n                    else:\n                        break\n\n                result = fetch_data(custom_url or url, http_head=(flags & FLAG_IMMUTABLE) > 0)\n                _count += 1\n\n                with cond:\n                    if result.bad:\n                        print(bad_url_str % id)\n                        if custom_tags:\n                            self.cur.execute('UPDATE bookmarks SET tags = ? WHERE id = ?', (custom_tags, id))\n                        continue\n\n                    if result.fetch_status in (del_error or []):\n                        if result.fetch_status in export_on:\n                            self._to_export[url] = self.get_rec_by_id(id, lock=False)\n                        LOGERR('HTTP error %s', result.fetch_status)\n                        self._to_delete += [id]\n                        if result.mime and self.chatty:\n                            print(mime_str % id)\n                        if custom_tags:\n                            self.cur.execute('UPDATE bookmarks SET tags = ? WHERE id = ?', (custom_tags, id))\n                        continue\n\n                    if result.mime:\n                        if self.chatty:\n                            print(mime_str % id)\n                        if custom_tags:\n                            self.cur.execute('UPDATE bookmarks SET tags = ? WHERE id = ?', (custom_tags, id))\n                        continue\n\n                    if not result.title:\n                        LOGERR(blank_url_str, id)\n                    elif update_title:\n                        query += ' metadata = ?,'\n                        arguments += (result.title,)\n\n                    if update_title and result.desc:\n                        query += ' desc = ?,'\n                        arguments += (result.desc,)\n\n                    _url = url\n                    if url_redirect and result.url != url:\n                        query += ' url = ?,'\n                        arguments += (result.url,)\n                        _url = result.url\n\n                    if result.fetch_status in export_on:\n                        self._to_export[_url] = url\n\n                    _tags = result.tags(keywords=False, redirect=tag_redirect, error=tag_error)\n                    if _tags:\n                        query += ' tags = ?,'\n                        arguments += (taglist_str((custom_tags or tags) + DELIM + _tags),)\n                    elif custom_tags:\n                        query += ' tags = ?,'\n                        arguments += (taglist_str(custom_tags),)\n\n                    if not arguments:  # nothing to update\n                        continue\n\n                    query = query[:-1] + ' WHERE id = ?'\n                    arguments += (id,)\n                    LOGDBG('refreshdb query: \"%s\", args: %s', query, arguments)\n\n                    self.cur.execute(query, arguments)\n\n                    # Save after fetching 32 titles per thread\n                    if _count % 32 == 0:\n                        self.conn.commit()\n\n                    if self.chatty:\n                        print(success_str % (result.title, id))\n\n                if INTERRUPTED:\n                    break\n\n            LOGDBG('Thread %d: processed %d', threading.get_ident(), _count)\n            with cond:\n                done['value'] += 1\n                processed['value'] += _count\n                cond.notify()\n\n        with self.lock:  # preventing external concurrent access\n            cond = threading.Condition()\n            with cond:  # preventing concurrent access between workers\n                threads = min(threads, recs)\n\n                for i in range(threads):\n                    thread = threading.Thread(target=refresh, args=(i, cond))\n                    thread.start()\n\n                while done['value'] < threads:\n                    cond.wait()\n                    LOGDBG('%d threads completed', done['value'])\n\n                # Guard: records found == total records processed\n                if recs != processed['value']:\n                    LOGERR('Records: %d, processed: %d !!!', recs, processed['value'])\n\n            if delay_delete:\n                self.conn.commit()\n            else:\n                self.commit_delete(retain_order=retain_order)\n\n        return True\n\n    def commit_delete(self, apply: bool = True, retain_order: bool = False):\n        \"\"\"Commit delayed delete commands.\"\"\"\n        if apply and self._to_delete is not None:\n            with self.lock:\n                for id in sorted(set(self._to_delete), reverse=True):\n                    self.delete_rec(id, delay_commit=True, chatty=False, retain_order=retain_order)\n                self.conn.commit()\n                self.cur.execute('VACUUM')\n        self._to_delete = None\n\n    def edit_update_rec(self, index, immutable=None):\n        \"\"\"Edit in editor and update a record.\n\n        Parameters\n        ----------\n        index : int\n            DB index of the record.\n            Last record, if index is -1.\n        immutable : bool, optional\n            Disable title fetch from web if True. Default is None (no change).\n\n        Returns\n        -------\n        bool\n            True if updated, else False.\n        \"\"\"\n\n        editor = get_system_editor()\n        if editor == 'none':\n            LOGERR('EDITOR must be set to use index with -w')\n            return False\n\n        if index == -1:\n            # Edit the last records\n            index = self.get_max_id()\n            if not index:\n                LOGERR('Empty database')\n                return False\n\n        rec = self.get_rec_by_id(index)\n        if not rec:\n            LOGERR('No matching index %d', index)\n            return False\n\n        # If reading from DB, show empty title and desc as empty lines. We have to convert because\n        # even in case of add with a blank title or desc, '' is used as initializer to show '-'.\n        result = edit_rec(editor, rec.url, rec.title or None, rec.tags_raw, rec.desc or None)\n        if result is not None:\n            url, title, tags, desc = result\n            return self.update_rec(index, url, title, tags, desc, immutable)\n\n        if immutable is not None:\n            return self.update_rec(index, immutable=immutable)\n\n        return False\n\n    def list_using_id(self, ids=[], order=['+id']):\n        \"\"\"List entries in the DB using the specified id list.\n\n        Parameters\n        ----------\n        ids : list of ids/ranges in string form\n        order : list of strings\n          Order description (fields from JSON export or DB, prepended with '+'/'-' for ASC/DESC).\n\n        Returns\n        -------\n        list\n        \"\"\"\n        q0 = 'SELECT * FROM bookmarks'\n        if ids:\n            q0 += ' WHERE id in ('\n            for idx in ids:\n                if '-' not in idx:\n                    q0 += idx + ','\n                else:\n                    val = idx.split('-')\n                    if val[0]:\n                        _range = list(map(int, val))\n                        _range[1] += 1\n                        part_ids = range(*_range)\n                    else:\n                        end = int(val[1])\n                        qtemp = 'SELECT id FROM bookmarks ORDER BY id DESC LIMIT {0}'.format(end)\n                        with self.lock:\n                            self.cur.execute(qtemp, [])\n                            part_ids = chain.from_iterable(self.cur.fetchall())\n                    q0 += ','.join(map(str, part_ids))\n            q0 = q0.strip(',')\n            q0 += ')'\n\n        try:\n            return self._fetch(q0 + f' ORDER BY {self._order(order)}')\n        except sqlite3.OperationalError as e:\n            LOGERR(e)\n            return []\n\n    def _search_tokens(self, keyword: str, deep=False, regex=False, markers=False):\n        \"\"\"Converts a keyword into a list of tokens, based on search parameters.\n        A token is a varied-length tuple of following values: (SQL field, deep, *SQL params).\"\"\"\n        deep = not regex and deep\n        if not markers or (re.sub(r'^\\*', '', keyword) and not re.match(r'^[.:>#]', keyword)):\n            s = (keyword if not markers else re.sub(r'^\\*', '', keyword))\n            if not s:\n                return []\n            tags = ([s] if regex and not markers else taglist(s.split(DELIM)))\n            return [('metadata', deep, s), ('url', deep, s), ('desc', deep, s)] + (tags and [('tags', deep, *tags)])\n        if re.match(r'^\\..', keyword):  # checking prefix + ensuring keyword[1:] is not empty\n            return [('metadata', deep, keyword[1:])]\n        if re.match(r'^:.', keyword):\n            return [('url', deep, keyword[1:])]\n        if re.match(r'^>.', keyword):\n            return [('desc', deep, keyword[1:])]\n        if re.match(r'^#,?[^,]', keyword):\n            tags = ([re.sub(r'^#,?', '', keyword)] if regex else taglist(keyword[1:].split(DELIM)))\n            return tags and [('tags', not keyword.startswith('#,'), *tags)]\n        return []\n\n    def _search_clause(self, tokens, regex=False) -> Tuple[str, List[str]]:\n        \"\"\"Converts a list of tokens into an SQL clause. (See also: BukuDb._search_tokens().)\n        If regex is True, the token is treated as a raw regex and the paired deep parameter is ignored.\"\"\"\n        border = lambda k, c: (',' if k == 'tags' else r'\\b' if c.isalnum() else '')\n\n        args, clauses = [], []\n        if regex:\n            for field, deep, param in tokens:\n                clauses += [field + ' REGEXP ?']\n                args += [param]\n        else:\n            for field, deep, *params in tokens:\n                _clauses = []\n                for param in params:\n                    if deep:\n                        _clauses += [field + \" LIKE ('%' || ? || '%')\"]\n                    else:\n                        _clauses += [field + ' REGEXP ?']\n                        param = border(field, param[0]) + re.escape(param) + border(field, param[-1])\n                    args += [param]\n                clauses += (_clauses if len(_clauses) < 2 else [f'({\" AND \".join(_clauses)})'])\n        return ' OR '.join(clauses), args\n\n    def searchdb(\n            self,\n            keywords: List[str],\n            all_keywords: bool = False,\n            deep: bool = False,\n            regex: bool = False,\n            markers: bool = False,\n            order: List[str] = ['+id'],\n    ) -> List[BookmarkVar]:\n        \"\"\"Search DB for entries where tags, URL, or title fields match keywords.\n\n        Parameters\n        ----------\n        keywords : list of str\n            Keywords to search.\n        order : list of str\n            Order description (fields from JSON export or DB, prepended with '+'/'-' for ASC/DESC).\n            Note: this applies to fields with the same number of matched keywords.\n        all_keywords : bool\n            False (default value) to return records matching ANY keyword.\n            True to return records matching ALL keywords. This also enables special\n            behaviour when keywords in (['blank'], ['immutable']).\n        deep : bool\n            True to search for matching substrings. Default is False.\n        markers : bool\n            True to use prefix markers for different fields. Default is False.\n        regex : bool\n            Match a regular expression if True. Default is False.\n            Overrides deep, all_keywords, and comma matching in tags with markers.\n\n        Returns\n        -------\n        list\n            List of search results.\n        \"\"\"\n        _order = self._order(order)\n        clauses, qargs = [], []\n        for keyword in keywords:\n            tokens = self._search_tokens(keyword, deep=deep, markers=markers)\n            clause, args = self._search_clause(tokens, regex=regex)\n            if clause and args:\n                clauses += [f'({clause})']\n                qargs += args\n        if not qargs:\n            return []\n\n        _count = lambda x: f'CASE WHEN {x} THEN 1 ELSE 0 END'\n        if regex:\n            query = ('SELECT id, url, metadata, tags, desc, flags\\nFROM (SELECT *, (' +\n                     '\\n    + '.join(map(_count, clauses)) +\n                     f') AS score\\n  FROM bookmarks WHERE score > 0 ORDER BY score DESC, {_order})')\n        elif all_keywords:\n            if keywords == ['blank']:\n                qargs, query = [DELIM], \"SELECT * FROM bookmarks WHERE metadata = '' OR tags = ?\"\n            elif keywords == ['immutable']:\n                qargs, query = [], 'SELECT * FROM bookmarks WHERE flags & 1 == 1'\n            else:\n                query = 'SELECT id, url, metadata, tags, desc, flags FROM bookmarks WHERE ' + '\\n  AND '.join(clauses)\n            query += f'\\nORDER BY {_order}'\n        elif not all_keywords:\n            query = ('SELECT id, url, metadata, tags, desc, flags\\nFROM (SELECT *, (' +\n                     '\\n    + '.join(map(_count, clauses)) +\n                     f') AS score\\n  FROM bookmarks WHERE score > 0 ORDER BY score DESC, {_order})')\n        else:\n            LOGERR('Invalid search option')\n            return []\n\n        LOGDBG('query: \"%s\", args: %s', query, qargs)\n\n        try:\n            return self._fetch(query, *qargs)\n        except sqlite3.OperationalError as e:\n            LOGERR(e)\n            return []\n\n    def search_by_tag(self, tags: Optional[str], order: List[str] = ['+id']) -> List[BookmarkVar]:\n        \"\"\"Search bookmarks for entries with given tags.\n\n        Parameters\n        ----------\n        tags : str\n            String of tags to search for.\n            Retrieves entries matching ANY tag if tags are\n            delimited with ','.\n            Retrieves entries matching ALL tags if tags are\n            delimited with '+'.\n        order : list of str\n            Order description (fields from JSON export or DB, prepended with '+'/'-' for ASC/DESC).\n            Note: this applies to fields with the same number of matched tags.\n\n        Returns\n        -------\n        list\n            List of search results.\n        \"\"\"\n\n        _order = self._order(order)\n        LOGDBG(tags)\n        if tags is None or tags == DELIM or tags == '':\n            return []\n\n        qargs, search_operator, excluded_tags = prep_tag_search(tags)\n        if search_operator is None:\n            LOGERR(\"Cannot use both '+' and ',' in same search\")\n            return []\n\n        LOGDBG('tags: %s', qargs)\n        LOGDBG('search_operator: %s', search_operator)\n        LOGDBG('excluded_tags: %s', excluded_tags)\n\n        if search_operator == 'AND':\n            query = ('SELECT id, url, metadata, tags, desc, flags FROM bookmarks WHERE (' +\n                     f' {search_operator} '.join(\"tags LIKE '%' || ? || '%'\" for tag in qargs) +\n                     ')' + ('' if not excluded_tags else ' AND tags NOT REGEXP ?') +\n                     f' ORDER BY {_order}')\n        else:\n            query = ('SELECT id, url, metadata, tags, desc, flags FROM (SELECT *, ' +\n                     ' + '.join(\"CASE WHEN tags LIKE '%' || ? || '%' THEN 1 ELSE 0 END\" for tag in qargs) +\n                     ' AS score FROM bookmarks WHERE score > 0' +\n                     ('' if not excluded_tags else ' AND tags NOT REGEXP ?') +\n                     f' ORDER BY score DESC, {_order})')\n        if excluded_tags:\n            qargs += [excluded_tags]\n\n        LOGDBG('query: \"%s\", args: %s', query, qargs)\n        return self._fetch(query, *qargs)\n\n    def search_keywords_and_filter_by_tags(\n            self,\n            keywords: List[str],\n            all_keywords: bool = False,\n            deep: bool = False,\n            regex: bool = False,\n            stag: Optional[List[str]] = None,\n            without: Optional[List[str]] = None,\n            markers: bool = False,\n            order: List[str] = ['+id']) -> List[BookmarkVar]:\n        \"\"\"Search bookmarks for entries with keywords and specified\n        criteria while filtering out entries with matching tags.\n\n        Parameters\n        ----------\n        keywords : list of str\n            Keywords to search.\n        without : list of str\n            Keywords to exclude; ignored if empty. Default is None.\n        all_keywords : bool\n            True to return records matching ALL keywords.\n            False to return records matching ANY keyword. (This is the default.)\n        deep : bool\n            True to search for matching substrings. Default is False\n        markers: bool\n            True to use prefix markers for different fields. Default is False.\n        regex : bool\n            Match a regular expression if True. Default is False.\n        stag : list of str\n            Strings of tags to search for. Default is None.\n            Retrieves entries matching ANY tag if tags are\n            delimited with ','.\n            Retrieves entries matching ALL tags if tags are\n            delimited with '+'.\n\n        Returns\n        -------\n        list\n            List of search results.\n        \"\"\"\n\n        results = self.searchdb(keywords, all_keywords=all_keywords, deep=deep, regex=regex, markers=markers, order=order)\n        results = (results if not stag else filter_from(results, self.search_by_tag(''.join(stag))))\n        return self.exclude_results_from_search(results, without, deep=deep, markers=markers)\n\n    def exclude_results_from_search(self, search_results, without, deep=False, markers=False):\n        \"\"\"Excludes records that match keyword search using without parameters\n\n        Parameters\n        ----------\n        search_results : list\n            List of search results.\n        without : list of str\n            Keywords to exclude. If empty, returning search_results unchanged.\n        deep : bool\n            True to search for matching substrings. Default is False.\n        markers: bool\n            True to use prefix markers for different fields. Default is False.\n\n        Returns\n        -------\n        list\n            List of search results.\n        \"\"\"\n\n        if not without:\n            return search_results\n        return filter_from(search_results, self.searchdb(without, deep=deep, markers=markers), exclude=True)\n\n    def swap_recs(self, index1: int, index2: int, *, lock: bool = True, delay_commit: bool = False):\n        \"\"\"Swaps two records with given indices\n\n        Parameters\n        ----------\n        index1 : int\n            Index of the 1st record to be exchanged.\n        index2 : int\n            Index of the 2nd record to be exchanged.\n        lock : bool\n            Whether to restrict concurrent access (True by default).\n        delay_commit : bool\n            True if record should not be committed to the DB,\n            leaving commit responsibility to caller. Default is False.\n\n        Returns\n        -------\n        bool\n            True on success, False on failure.\n        \"\"\"\n        if lock:\n            with self.lock:\n                return self.swap_recs(index1, index2, lock=False, delay_commit=delay_commit)\n\n        max_id = self.get_max_id()\n        if not max_id or index1 == index2 or not all(0 < x <= max_id for x in [index1, index2]):\n            return False\n\n        self.cur.executemany('UPDATE bookmarks SET id = ? WHERE id = ?',\n                             [(max_id+1, index1), (index1, index2), (index2, max_id+1)])\n        if not delay_commit:\n            self.conn.commit()\n        return True\n\n    def compactdb(self, index: int, delay_commit: bool = False, upto: Optional[int] = None, retain_order: bool = False):\n        \"\"\"When an entry at index is deleted, move the\n        last entry in DB to index, if index is lesser.\n\n        Parameters\n        ----------\n        index : int\n            DB index of deleted entry.\n        delay_commit : bool\n            True if record should not be committed to the DB,\n            leaving commit responsibility to caller. Default is False.\n        upto : int, optional\n            If specified, multiple indices are moved at once.\n        retain_order: bool\n            Shift indices of multiple records by 1 instead of replacing\n            the deleted record with the last one. Default is False.\n        \"\"\"\n\n        # Return if the last index left in DB was just deleted\n        max_id = self.get_max_id()\n        if not max_id or (upto and upto < index):\n            return\n\n        # NOOP if the just deleted index was the last one\n        if max_id > index:\n            with self.lock:\n                if retain_order or (upto or 0) > index:\n                    step = (max(max_id - upto, upto + 1 - index) if not retain_order else\n                            1 if not upto else upto + 1 - index)\n                    self.cur.execute('UPDATE bookmarks SET id = id-? WHERE id >= ?', (step, index+step))\n                    msg = f'Indices {index+step}-{max_id} moved to {index}-{max_id-step}'\n                else:\n                    self.cur.execute('UPDATE bookmarks SET id = ? WHERE id = ?', (index, max_id))\n                    msg = f'Index {max_id} moved to {index}'\n                if not delay_commit:\n                    self.conn.commit()\n                    self.cur.execute('VACUUM')\n            if self.chatty:\n                print(msg)\n\n    def delete_rec(\n            self,\n            index: int = None,\n            low: int = 0,\n            high: int = 0,\n            is_range: bool = False,\n            delay_commit: bool = False,\n            chatty: Optional[bool] = None,\n            retain_order: bool = False,\n    ) -> bool:\n        \"\"\"Delete a single record or remove the table if index is 0.\n\n        Parameters\n        ----------\n        index : int, optional\n            DB index of deleted entry.\n        low : int\n            Actual lower index of range.\n        high : int\n            Actual higher index of range.\n        is_range : bool\n            A range is passed using low and high arguments.\n            An index is ignored if is_range is True.\n        delay_commit : bool\n            True if record should not be committed to the DB,\n            leaving commit responsibility to caller. Default is False.\n        chatty : Optional[bool]\n            Override for self.chatty\n        retain_order: bool\n            Shift indices of multiple records instead of replacing\n            the deleted record with the last one. Default is False.\n\n        Raises\n        ------\n        TypeError\n            If any of index, low, or high variable is not integer.\n\n        Returns\n        -------\n        bool\n            True on success, False on failure.\n\n        Examples\n        --------\n        >>> from tempfile import NamedTemporaryFile\n        >>> import buku\n        >>> sdb = buku.BukuDb(dbfile=NamedTemporaryFile().name)  # single record database\n        >>> sdb.add_rec('https://example.com')\n        1\n        >>> sdb.delete_rec(1)\n        Index 1 deleted\n        True\n\n        Delete record with default range.\n\n        >>> sdb = buku.BukuDb(dbfile=NamedTemporaryFile().name)\n        >>> sdb.add_rec('https://example.com')\n        1\n        >>> sdb.delete_rec(is_range=True)  # doctest: +SKIP\n        Remove ALL bookmarks? (y/n): y\n        All bookmarks deleted\n        True\n\n        Running the function without any parameter will raise TypeError.\n\n        >>> sdb = buku.BukuDb(dbfile=NamedTemporaryFile().name)\n        >>> sdb.add_rec('https://example.com')\n        1\n        >>> sdb.delete_rec()\n        Traceback (most recent call last):\n        ...\n        TypeError: index, low, or high variable is not integer\n\n        Negative number on `high` and `low` parameters when is_range is True\n        will log error and return False\n\n        >>> edb = buku.BukuDb(dbfile=NamedTemporaryFile().name)\n        >>> edb.delete_rec(low=-1, high=-1, is_range=True)\n        False\n\n        Remove the table\n\n        >>> sdb = buku.BukuDb(dbfile=NamedTemporaryFile().name)\n        >>> sdb.delete_rec(0)  # doctest: +SKIP\n        Remove ALL bookmarks? (y/n): y\n        All bookmarks deleted\n        True\n        \"\"\"\n        chatty = (chatty if chatty is not None else self.chatty)\n        params = [low, high]\n        if not is_range:\n            params.append(index)\n        if any(map(lambda x: not isinstance(x, int), params)):\n            raise TypeError('index, low, or high variable is not integer')\n\n        if is_range:  # Delete a range of indices\n            if low < 0 or high < 0:\n                LOGERR('Negative range boundary')\n                return False\n\n            if low > high:\n                low, high = high, low\n\n            # If range starts from 0, delete all records\n            if low == 0:\n                return self.cleardb()\n\n            try:\n                if chatty:\n                    with self.lock:\n                        self.cur.execute('SELECT COUNT(*) from bookmarks where id '\n                                         'BETWEEN ? AND ?', (low, high))\n                        count = self.cur.fetchone()\n                    if count[0] < 1:\n                        print('Index %d-%d: 0 deleted' % (low, high))\n                        return False\n\n                    if self.print_rec(0, low, high, True) is True:\n                        resp = input('Delete these bookmarks? (y/n): ')\n                        if resp != 'y':\n                            return False\n\n                query = 'DELETE from bookmarks where id BETWEEN ? AND ?'\n                with self.lock:\n                    self.cur.execute(query, (low, high))\n                    print('Index %d-%d: %d deleted' % (low, high, self.cur.rowcount))\n                    if not self.cur.rowcount:\n                        return False\n\n                # Compact DB in a single operation for the range\n                # Delayed commit is forced\n                with self.lock:\n                    self.compactdb(low, upto=high, delay_commit=True, retain_order=retain_order)\n                    if not delay_commit:\n                        self.conn.commit()\n                        self.cur.execute('VACUUM')\n            except IndexError:\n                LOGERR('No matching index')\n                return False\n        elif index == 0:  # Remove the table\n            return self.cleardb()\n        else:  # Remove a single entry\n            try:\n                if chatty:\n                    with self.lock:\n                        self.cur.execute('SELECT COUNT(*) FROM bookmarks WHERE '\n                                         'id = ? LIMIT 1', (index,))\n                        count = self.cur.fetchone()\n                    if count[0] < 1:\n                        LOGERR('No matching index %d', index)\n                        return False\n\n                    if self.print_rec(index) is True:\n                        resp = input('Delete this bookmark? (y/n): ')\n                        if resp != 'y':\n                            return False\n\n                with self.lock:\n                    query = 'DELETE FROM bookmarks WHERE id = ?'\n                    self.cur.execute(query, (index,))\n                    if self.cur.rowcount == 1:\n                        print('Index %d deleted' % index)\n                        self.compactdb(index, delay_commit=True, retain_order=retain_order)\n                        if not delay_commit:\n                            self.conn.commit()\n                            self.cur.execute('VACUUM')\n                    else:\n                        LOGERR('No matching index %d', index)\n                        return False\n            except IndexError:\n                LOGERR('No matching index %d', index)\n                return False\n            except sqlite3.OperationalError as e:\n                LOGERR(e)\n                return False\n\n        return True\n\n    def delete_resultset(self, results, retain_order=False):\n        \"\"\"Delete search results in descending order of DB index.\n\n        Indices are expected to be unique and in ascending order.\n\n        Notes\n        -----\n            This API forces a delayed commit.\n\n        Parameters\n        ----------\n        results : list of tuples\n            List of results to delete from DB.\n        retain_order: bool\n            Shift indices of multiple records instead of replacing\n            the deleted record with the last one. Default is False.\n\n        Returns\n        -------\n        bool\n            True on success, False on failure.\n        \"\"\"\n        if self.chatty:\n            resp = read_in('Delete the search results? (y/n): ')\n            if resp != 'y':\n                return False\n\n        # delete records in reverse order\n        ids = sorted(set(x[0] for x in results))\n        with self.lock:\n            for pos, id in reversed(list(enumerate(ids))):\n                self.delete_rec(id, delay_commit=True, retain_order=retain_order)\n\n                # Commit at every 200th removal, counting from the end\n                if pos % 200 == 0:\n                    self.conn.commit()\n                    self.cur.execute('VACUUM')\n\n        return True\n\n    def delete_rec_all(self, delay_commit=False):\n        \"\"\"Removes all records in the Bookmarks table.\n\n        Parameters\n        ----------\n        delay_commit : bool\n            True if record should not be committed to the DB,\n            leaving commit responsibility to caller. Default is False.\n\n        Returns\n        -------\n        bool\n            True on success, False on failure.\n        \"\"\"\n\n        try:\n            with self.lock:\n                self.cur.execute('DELETE FROM bookmarks')\n                if not delay_commit:\n                    self.conn.commit()\n                    self.cur.execute('VACUUM')\n            return True\n        except Exception as e:\n            LOGERR('delete_rec_all(): %s', e)\n            return False\n\n    def cleardb(self, confirm=True):\n        \"\"\"Drops the bookmark table if it exists.\n\n        Parameters\n        ----------\n        confirm : bool\n            False if confirmation prompt is required. Default is True.\n\n        Returns\n        -------\n        bool\n            True on success, False on failure.\n        \"\"\"\n\n        resp = ('y' if not confirm else read_in('Remove ALL bookmarks? (y/n): '))\n        if resp != 'y':\n            print('No bookmarks deleted')\n            return False\n\n        if self.delete_rec_all():\n            print('All bookmarks deleted')\n            return True\n\n        return False\n\n    def print_rec(self, index: Optional[IntOrInts] = 0,\n                  low: int = 0, high: int = 0, is_range: bool = False, order: List[str] = []) -> bool:\n        \"\"\"Print bookmark details at index or all bookmarks if index is 0.\n\n        A negative index behaves like tail, if title is blank show \"Untitled\".\n\n        Empty database check will run when `index` < 0 and `is_range` is False.\n\n        Parameters\n        -----------\n        index : int | int[] | int{} | range, optional\n            DB index(es) of record(s) to print. 0 or empty prints all records.\n            Negative value prints out last `index` rows.\n        low : int\n            Actual lower index of range.\n        high : int\n            Actual higher index of range.\n        is_range : bool\n            A range is passed using low and high arguments.\n            An index is ignored if is_range is True.\n        order : list of str\n            Order description (fields from JSON export or DB, prepended with '+'/'-' for ASC/DESC).\n\n        Returns\n        -------\n        bool\n            True on success, False on failure.\n\n        Examples\n        --------\n        >>> import buku\n        >>> from tempfile import NamedTemporaryFile\n        >>> edb = buku.BukuDb(dbfile=NamedTemporaryFile().name)  # empty database\n        >>> edb.print_rec()\n        True\n\n        Print negative index on empty database will log error and return False\n\n        >>> edb.print_rec(-3)\n        False\n\n        print non empty database with default argument.\n\n        >>> sdb = buku.BukuDb(dbfile=NamedTemporaryFile().name)  # single record database\n        >>> sdb.add_rec('https://example.com')\n        1\n        >>> assert sdb.print_rec()\n        1. Example Domain\n           > https://example.com\n        <BLANKLINE>\n\n        Negative number on `high` and `low` parameters when is_range is True\n        will log error and return False\n\n        >>> sdb.print_rec(low=-1, high=-1, is_range=True)\n        False\n        >>> edb.print_rec(low=-1, high=-1, is_range=True)\n        False\n        \"\"\"\n        if isinstance(index, range) and index.step == 1 and index.start != 0:  # low=0 triggers custom behaviour\n            return self.print_rec(None, is_range=True, low=index.start, high=index.stop-1, order=order)\n\n        if not is_range and isinstance(index, int) and index < 0:\n            # Show the last n records\n            _id = self.get_max_id()\n            if not _id:\n                LOGERR('Empty database')\n                return False\n\n            low = (1 if _id <= -index else _id + index + 1)\n            return self.print_rec(None, is_range=True, low=low, high=_id, order=order)\n\n        _order = self._order(order)\n        if is_range:\n            if low < 0 or high < 0:\n                LOGERR('Negative range boundary')\n                return False\n\n            if low > high:\n                low, high = high, low\n\n            try:\n                # If range starts from 0 print all records\n                with self.lock:\n                    if low == 0:\n                        query = f'SELECT * from bookmarks ORDER BY {_order}'\n                        resultset = self.cur.execute(query)\n                    else:\n                        query = f'SELECT * from bookmarks where id BETWEEN ? AND ? ORDER BY {_order}'\n                        resultset = self.cur.execute(query, (low, high))\n            except IndexError:\n                LOGERR('Index out of range')\n                return False\n        elif index:  # Show record at index\n            try:\n                if isinstance(index, int):\n                    results = self._fetch('SELECT * FROM bookmarks WHERE id = ? LIMIT 1', index)\n                else:\n                    placeholder = ', '.join(['?'] * len(index))\n                    results = self._fetch(f'SELECT * FROM bookmarks WHERE id IN ({placeholder}) ORDER BY {_order}', *index)\n            except IndexError:\n                results = None\n            if not results:\n                LOGERR('No matching index %s', index)\n                return False\n\n            single_record = len(results) == 1\n            if self.json is None:\n                print_rec_with_filter(results, self.field_filter)\n            elif self.json:\n                write_string_to_file(format_json(results, single_record, field_filter=self.field_filter), self.json)\n            else:\n                print_json_safe(results, single_record, field_filter=self.field_filter)\n\n            return True\n        else:  # Show all entries\n            with self.lock:\n                self.cur.execute(f'SELECT * FROM bookmarks ORDER BY {_order}')\n                resultset = self.cur.fetchall()\n\n        if not resultset:\n            LOGERR('0 records')\n            return True\n\n        if self.json is None:\n            print_rec_with_filter(resultset, self.field_filter)\n        elif self.json:\n            write_string_to_file(format_json(resultset, field_filter=self.field_filter), self.json)\n        else:\n            print_json_safe(resultset, field_filter=self.field_filter)\n\n        return True\n\n    def get_tag_all(self):\n        \"\"\"Get list of tags in DB.\n\n        Returns\n        -------\n        tuple\n            (list of unique tags sorted alphabetically,\n             dictionary of {tag: usage_count}).\n        \"\"\"\n\n        tags = []\n        unique_tags = []\n        dic = {}\n        qry = 'SELECT DISTINCT tags, COUNT(tags) FROM bookmarks GROUP BY tags'\n        with self.lock:\n            for row in self.cur.execute(qry):\n                tagset = row[0].strip(DELIM).split(DELIM)\n                for tag in tagset:\n                    if tag not in tags:\n                        dic[tag] = row[1]\n                        tags += (tag,)\n                    else:\n                        dic[tag] += row[1]\n\n        if not tags:\n            return tags, dic\n\n        if tags[0] == '':\n            unique_tags = sorted(tags[1:])\n        else:\n            unique_tags = sorted(tags)\n\n        return unique_tags, dic\n\n    def suggest_similar_tag(self, tagstr):\n        \"\"\"Show list of tags those go together in DB.\n\n        Parameters\n        ----------\n        tagstr : str\n            Original tag string.\n\n        Returns\n        -------\n        str\n            DELIM separated string of tags.\n        \"\"\"\n\n        if not tagstr:\n            return ''\n        tags = tagstr.split(',')\n\n        qry = 'SELECT DISTINCT tags FROM bookmarks WHERE tags LIKE ?'\n        tagset = set()\n        for tag in tags:\n            if tag == '':\n                continue\n\n            with self.lock:\n                self.cur.execute(qry, ('%' + delim_wrap(tag) + '%',))\n                results = self.cur.fetchall()\n            for row in results:\n                # update tagset with unique tags in row\n                tagset |= set(row[0].strip(DELIM).split(DELIM))\n\n        # remove user supplied tags from tagset\n        tagset.difference_update(tags)\n\n        if not len(tagset):\n            return tagstr\n\n        unique_tags = sorted(tagset)\n\n        print('similar tags:\\n')\n        for count, tag in enumerate(unique_tags):\n            print('%d. %s' % (count + 1, tag))\n\n        selected_tags = input('\\nselect: ').split()\n        print()\n        if not selected_tags:\n            return tagstr\n\n        tags = [tagstr]\n        for index in selected_tags:\n            try:\n                tags.append(delim_wrap(unique_tags[int(index) - 1]))\n            except Exception as e:\n                LOGERR(e)\n                continue\n\n        return parse_tags(tags)\n\n    def replace_tag(self, orig: str, new: List[str] = []):\n        \"\"\"Replace original tag by new tags in all records.\n\n        Remove original tag if new tag is empty.\n\n        Parameters\n        ----------\n        orig : str\n            Original tag.\n        new : list\n            Replacement tags.\n\n        Raises\n        -------\n        ValueError: Invalid input(s) provided.\n        RuntimeError: Tag deletion failed.\n\n        \"\"\"\n\n        if DELIM in orig:\n            raise ValueError(\"Original tag cannot contain delimiter ({}).\".format(DELIM))\n\n        orig = delim_wrap(orig)\n        newtags = taglist_str(DELIM.join(new))\n\n        if orig == newtags:\n            raise ValueError(\"Original and replacement tags are the same.\")\n\n        # Remove original tag from DB if new tagset reduces to delimiter\n        if newtags == DELIM:\n            if not self.delete_tag_at_index(0, orig, chatty=self.chatty):\n                raise RuntimeError(\"Tag deletion failed.\")\n\n        # Update bookmarks with original tag\n        with self.lock:\n            query = 'SELECT id, tags FROM bookmarks WHERE tags LIKE ?'\n            self.cur.execute(query, ('%' + orig + '%',))\n            results = self.cur.fetchall()\n            if results:\n                query = 'UPDATE bookmarks SET tags = ? WHERE id = ?'\n                for row in results:\n                    tags = row[1].replace(orig, newtags)\n                    tags = parse_tags([tags])\n                    self.cur.execute(query, (tags, row[0],))\n                    print('Index %d updated' % row[0])\n\n                self.conn.commit()\n\n    def get_tagstr_from_taglist(self, id_list, taglist):\n        \"\"\"Get a string of delimiter-separated (and enclosed) string\n        of tags from a dictionary of tags by matching ids.\n\n        The inputs are the outputs from BukuDb.get_tag_all().\n\n        Parameters\n        ----------\n        id_list : list\n            List of ids.\n        taglist : list\n            List of tags.\n        Returns\n        -------\n        str\n            Delimiter separated and enclosed list of tags.\n        \"\"\"\n\n        tags = DELIM\n\n        for id in id_list:\n            if is_int(id) and int(id) > 0:\n                tags += taglist[int(id) - 1] + DELIM\n            elif '-' in id:\n                vals = [int(x) for x in id.split('-')]\n                if vals[0] > vals[-1]:\n                    vals[0], vals[-1] = vals[-1], vals[0]\n\n                for _id in range(vals[0], vals[-1] + 1):\n                    tags += taglist[_id - 1] + DELIM\n\n        return tags\n\n    def set_tag(self, cmdstr, taglist):\n        \"\"\"Append, overwrite, remove tags using the symbols >>, > and << respectively.\n\n        Parameters\n        ----------\n        cmdstr : str\n            Command pattern.\n        taglist : list\n            List of tags.\n\n        Returns\n        -------\n        int\n            Number of indices updated on success, -1 on failure, -2 on no symbol found.\n        \"\"\"\n\n        if not cmdstr or not taglist:\n            return -1\n\n        flag = 0  # 0: invalid, 1: append, 2: overwrite, 3: remove\n        index = cmdstr.find('>>')\n        if index == -1:\n            index = cmdstr.find('>')\n            if index != -1:\n                flag = 2\n            else:\n                index = cmdstr.find('<<')\n                if index != -1:\n                    flag = 3\n        else:\n            flag = 1\n\n        if not flag:\n            return -2\n\n        tags = DELIM\n        id_list = cmdstr[:index].split()\n        try:\n            tags = self.get_tagstr_from_taglist(id_list, taglist)\n            if tags == DELIM and flag != 2:\n                return -1\n        except ValueError:\n            return -1\n\n        if flag != 2:\n            index += 1\n\n        with self.lock:\n            update_count = 0\n            query = 'UPDATE bookmarks SET tags = ? WHERE id = ?'\n            try:\n                db_id_list = cmdstr[index + 1:].split()\n                for id in db_id_list:\n                    if is_int(id) and int(id) > 0:\n                        if flag == 1:\n                            if self.append_tag_at_index(id, tags, True):\n                                update_count += 1\n                        elif flag == 2:\n                            tags = parse_tags([tags])\n                            self.cur.execute(query, (tags, id,))\n                            update_count += self.cur.rowcount\n                        else:\n                            self.delete_tag_at_index(id, tags, True)\n                            update_count += 1\n                    elif '-' in id:\n                        vals = [int(x) for x in id.split('-')]\n                        if vals[0] > vals[-1]:\n                            vals[0], vals[-1] = vals[-1], vals[0]\n\n                        for _id in range(vals[0], vals[-1] + 1):\n                            if flag == 1:\n                                if self.append_tag_at_index(_id, tags, True):\n                                    update_count += 1\n                            elif flag == 2:\n                                tags = parse_tags([tags])\n                                self.cur.execute(query, (tags, _id,))\n                                update_count += self.cur.rowcount\n                            else:\n                                if self.delete_tag_at_index(_id, tags, True):\n                                    update_count += 1\n                    else:\n                        return -1\n            except ValueError:\n                return -1\n            except sqlite3.IntegrityError:\n                return -1\n\n            try:\n                self.conn.commit()\n            except Exception as e:\n                LOGERR(e)\n                return -1\n\n        return update_count\n\n    def browse_by_index(self, index=0, low=0, high=0, is_range=False):\n        \"\"\"Open URL at index or range of indices in browser.\n\n        Parameters\n        ----------\n        index : int\n            Index to browse. 0 opens a random bookmark.\n        low : int\n            Actual lower index of range.\n        high : int\n            Higher index of range.\n        is_range : bool\n            A range is passed using low and high arguments.\n            If True, index is ignored. Default is False.\n\n        Returns\n        -------\n        bool\n            True on success, False on failure.\n        \"\"\"\n\n        if is_range:\n            if low < 0 or high < 0:\n                LOGERR('Negative range boundary')\n                return False\n\n            if low > high:\n                low, high = high, low\n\n            try:\n                # If range starts from 0 throw an error\n                if low <= 0:\n                    raise IndexError\n\n                qry = 'SELECT URL from bookmarks where id BETWEEN ? AND ?'\n                with self.lock:\n                    for row in self.cur.execute(qry, (low, high)):\n                        browse(row[0], self.default_scheme)\n                return True\n            except IndexError:\n                LOGERR('Index out of range')\n                return False\n\n        if index < 0:\n            LOGERR('Invalid index %d', index)\n            return False\n\n        if index == 0:\n            max_id = self.get_max_id()\n            if not max_id:\n                print('No bookmarks added yet ...')\n                return False\n\n            index = random.randint(1, max_id)\n            LOGDBG('Opening random index %d', index)\n\n        qry = 'SELECT URL FROM bookmarks WHERE id = ? LIMIT 1'\n        try:\n            with self.lock:\n                for row in self.cur.execute(qry, (index,)):\n                    browse(row[0], self.default_scheme)\n                    return True\n            LOGERR('No matching index %d', index)\n        except IndexError:\n            LOGERR('No matching index %d', index)\n\n        return False\n\n    def exportdb(self, filepath: str, resultset: Optional[List[BookmarkVar]] = None,\n                 order: List[str] = ['id'], pick: Optional[int] = None) -> bool:\n        \"\"\"Export DB bookmarks to file.\n        Exports full DB, if resultset is None.\n        Additionally, if run after a (batch) update with export_on, only export those records.\n\n        If destination file name ends with '.db', bookmarks are\n        exported to a buku database file.\n        If destination file name ends with '.md', bookmarks are\n        exported to a Markdown file.\n        If destination file name ends with '.org' bookmarks are\n        exported to an org file.\n        If destination file name ends with '.xbel' bookmarks are\n        exported to a XBEL file.\n        If destination file name ends with '.rss'/'.atom' bookmarks are\n        exported to an RSS file.\n        Otherwise, bookmarks are exported to a Firefox bookmarks.html\n        formatted file.\n\n        Parameters\n        ----------\n        filepath : str\n            Path to export destination file.\n        resultset : list of tuples\n            List of results to export. Use `None` to get current DB.\n            Ignored if run after a (batch) update with export_on.\n        order : list of str\n            Order description (fields from JSON export or DB, prepended with '+'/'-' for ASC/DESC).\n        pick : int, optional\n            Reduce the export to a random subset of up to given (positive) size. Default is None.\n\n\n        Returns\n        -------\n        bool\n            True on success, False on failure.\n        \"\"\"\n\n        count = 0\n\n        if not resultset:\n            resultset = self.get_rec_all(order=order)\n            if not resultset:\n                print('No records found')\n                return False\n\n        old = self._to_export or {}\n        if self._to_export is not None:\n            _resultset = dict(old)\n            _resultset.update({x.url: x for x in resultset if x.url in old})\n            resultset = self._sort(_resultset.values(), order)\n            self._to_export = None\n            if not resultset:\n                print('No records to export')\n                return False\n\n        if pick and pick < len(resultset):\n            resultset = self._sort(random.sample(resultset, pick), order)\n\n        if os.path.exists(filepath):\n            resp = read_in(filepath + ' exists. Overwrite? (y/n): ')\n            if resp != 'y':\n                return False\n\n            if filepath.endswith('.db'):\n                os.remove(filepath)\n\n        if filepath.endswith('.db'):\n            outdb = BukuDb(dbfile=filepath)\n            qry = 'INSERT INTO bookmarks(URL, metadata, tags, desc, flags) VALUES (?, ?, ?, ?, ?)'\n            for row in resultset:\n                _old = old.get(row.url)\n                _add = (f' (OLD URL = {_old})' if isinstance(_old, str) and _old != row.url else\n                        ' (DELETED)' if _old is row else '')\n                title = ((row.title or '') + _add if _add else row.title)\n                outdb.cur.execute(qry, (row.url, title, row.tags_raw, row.desc, row.flags))\n                count += 1\n            outdb.conn.commit()\n            outdb.close()\n            print('%s exported' % count)\n            return True\n\n        with open(filepath, mode='w', encoding='utf-8') as outfp:\n            res = {}  # type: Dict\n            if filepath.endswith('.md'):\n                res = convert_bookmark_set(resultset, 'markdown', old)\n                count += res['count']\n                outfp.write(res['data'])\n            elif filepath.endswith('.org'):\n                res = convert_bookmark_set(resultset, 'org', old)\n                count += res['count']\n                outfp.write(res['data'])\n            elif filepath.endswith('.xbel'):\n                res = convert_bookmark_set(resultset, 'xbel', old)\n                count += res['count']\n                outfp.write(res['data'])\n            elif filepath.endswith('.rss') or filepath.endswith('.atom'):\n                res = convert_bookmark_set(resultset, 'rss', old)\n                count += res['count']\n                outfp.write(res['data'])\n            else:\n                res = convert_bookmark_set(resultset, 'html', old)\n                count += res['count']\n                outfp.write(res['data'])\n            print('%s exported' % count)\n            return True\n        return False\n\n    def traverse_bm_folder(self, sublist, unique_tag, folder_name, add_parent_folder_as_tag):\n        \"\"\"Traverse bookmark folders recursively and find bookmarks.\n\n        Parameters\n        ----------\n        sublist : list\n            List of child entries in bookmark folder.\n        unique_tag : str\n            Timestamp tag in YYYYMonDD format.\n        folder_name : str\n            Name of the parent folder.\n        add_parent_folder_as_tag : bool\n            True if bookmark parent folders should be added as tags else False.\n\n        Returns\n        -------\n        tuple\n            Bookmark record data.\n        \"\"\"\n\n        for item in sublist:\n            if item['type'] == 'folder':\n                next_folder_name = folder_name + DELIM + strip_delim(item['name'])\n                yield from self.traverse_bm_folder(\n                        item['children'],\n                        unique_tag,\n                        next_folder_name,\n                        add_parent_folder_as_tag)\n            elif item['type'] == 'url':\n                try:\n                    if is_nongeneric_url(item['url']):\n                        continue\n                except KeyError:\n                    continue\n\n                tags = ''\n                if add_parent_folder_as_tag:\n                    tags += folder_name\n                if unique_tag:\n                    tags += DELIM + unique_tag\n                yield (item['url'], item['name'], parse_tags([tags]), None, 0, True, False)\n\n    def load_chrome_database(self, path, unique_tag, add_parent_folder_as_tag):\n        \"\"\"Open Chrome Bookmarks JSON file and import data.\n\n        Parameters\n        ----------\n        path : str\n            Path to Google Chrome bookmarks file.\n        unique_tag : str\n            Timestamp tag in YYYYMonDD format.\n        add_parent_folder_as_tag : bool\n            True if bookmark parent folders should be added as tags else False.\n        \"\"\"\n\n        with open(path, 'r', encoding=\"utf8\") as datafile:\n            data = json.load(datafile)\n\n        roots = data['roots']\n        for entry in roots:\n            # Needed to skip 'sync_transaction_version' key from roots\n            if isinstance(roots[entry], str):\n                continue\n            for item in self.traverse_bm_folder(\n                    roots[entry]['children'],\n                    unique_tag,\n                    roots[entry]['name'],\n                    add_parent_folder_as_tag):\n                self.add_rec(*item)\n\n    def load_firefox_database(self, path, unique_tag, add_parent_folder_as_tag):\n        \"\"\"Connect to Firefox sqlite db and import bookmarks into BukuDb.\n\n        Parameters\n        ----------\n        path : str\n            Path to Firefox bookmarks sqlite database.\n        unique_tag : str\n            Timestamp tag in YYYYMonDD format.\n        add_parent_folder_as_tag : bool\n            True if bookmark parent folders should be added as tags else False.\n        \"\"\"\n\n        # Connect to input DB\n        conn = sqlite3.connect('file:%s?mode=ro' % path, uri=True)\n\n        cur = conn.cursor()\n        res = cur.execute('SELECT DISTINCT fk, parent, title FROM moz_bookmarks WHERE type=1')\n        # get id's and remove duplicates\n        for row in res.fetchall():\n            # get the url\n            res = cur.execute('SELECT url FROM moz_places where id={}'.format(row[0]))\n            url = res.fetchone()[0]\n            if is_nongeneric_url(url):\n                continue\n\n            # get tags\n            res = cur.execute('SELECT parent FROM moz_bookmarks WHERE '\n                              'fk={} AND title IS NULL'.format(row[0]))\n            bm_tag_ids = [tid for item in res.fetchall() for tid in item]\n\n            bookmark_tags = []\n            for bm_tag_id in bm_tag_ids:\n                res = cur.execute('SELECT title FROM moz_bookmarks WHERE id={}'.format(bm_tag_id))\n                bookmark_tags.append(res.fetchone()[0])\n\n            if add_parent_folder_as_tag:\n                # add folder name\n                parent_id = row[1]\n                while parent_id:\n                    res = cur.execute('SELECT title,parent FROM moz_bookmarks '\n                                      'WHERE id={}'.format(parent_id))\n                    parent = res.fetchone()\n                    if parent:\n                        title, parent_id = parent\n                        bookmark_tags.append(title)\n\n            if unique_tag:\n                # add timestamp tag\n                bookmark_tags.append(unique_tag)\n\n            formatted_tags = [DELIM + strip_delim(tag) for tag in bookmark_tags]\n            tags = parse_tags(formatted_tags)\n\n            # get the title\n            title = row[2] or ''\n\n            self.add_rec(url, title, tags, None, 0, True, False)\n        try:\n            cur.close()\n            conn.close()\n        except Exception as e:\n            LOGERR(e)\n\n    def load_edge_database(self, path, unique_tag, add_parent_folder_as_tag):\n        \"\"\"Open Edge Bookmarks JSON file and import data.\n\n        Parameters\n        ----------\n        path : str\n            Path to Microsoft Edge bookmarks file.\n        unique_tag : str\n            Timestamp tag in YYYYMonDD format.\n        add_parent_folder_as_tag : bool\n            True if bookmark parent folders should be added as tags else False.\n        \"\"\"\n\n        with open(path, 'r', encoding=\"utf8\") as datafile:\n            data = json.load(datafile)\n\n        roots = data['roots']\n        for entry in roots:\n            # Needed to skip 'sync_transaction_version' key from roots\n            if isinstance(roots[entry], str):\n                continue\n            for item in self.traverse_bm_folder(\n                    roots[entry]['children'],\n                    unique_tag,\n                    roots[entry]['name'],\n                    add_parent_folder_as_tag):\n                self.add_rec(*item)\n\n    def auto_import_from_browser(self, firefox_profile=None):\n        \"\"\"Import bookmarks from a browser default database file.\n\n        Supports Firefox, Google Chrome, Chromium, Vivaldi, Brave, and MS Edge.\n\n        Returns\n        -------\n        bool\n            True on success, False on failure.\n        \"\"\"\n\n        if sys.platform.startswith(('linux', 'freebsd', 'openbsd')):\n            gc_bm_db_path = '~/.config/google-chrome/Default/Bookmarks'\n            cb_bm_db_path = '~/.config/chromium/Default/Bookmarks'\n            vi_bm_db_path = '~/.config/vivaldi/Default/Bookmarks'\n            br_bm_db_path = '~/.config/BraveSoftware/Brave-Browser/Default/Bookmarks'\n            me_bm_db_path = '~/.config/microsoft-edge/Default/Bookmarks'\n            default_ff_folder = '~/.mozilla/firefox'\n        elif sys.platform == 'darwin':\n            gc_bm_db_path = '~/Library/Application Support/Google/Chrome/Default/Bookmarks'\n            cb_bm_db_path = '~/Library/Application Support/Chromium/Default/Bookmarks'\n            vi_bm_db_path = '~/Library/Application Support/Vivaldi/Default/Bookmarks'\n            br_bm_db_path = '~/Library/Application Support/BraveSoftware/Brave-Browser/Default/Bookmarks'\n            me_bm_db_path = '~/Library/Application Support/Microsoft Edge/Default/Bookmarks'\n            default_ff_folder = '~/Library/Application Support/Firefox'\n        elif sys.platform == 'win32':\n            gc_bm_db_path = os.path.expandvars('%LOCALAPPDATA%/Google/Chrome/User Data/Default/Bookmarks')\n            cb_bm_db_path = os.path.expandvars('%LOCALAPPDATA%/Chromium/User Data/Default/Bookmarks')\n            vi_bm_db_path = os.path.expandvars('%LOCALAPPDATA%/Vivaldi/User Data/Default/Bookmarks')\n            br_bm_db_path = os.path.expandvars('%LOCALAPPDATA%/BraveSoftware/Brave-Browser/User Data/Default/Bookmarks')\n            me_bm_db_path = os.path.expandvars('%LOCALAPPDATA%/Microsoft/Edge/User Data/Default/Bookmarks')\n            default_ff_folder = os.path.expandvars('%APPDATA%/Mozilla/Firefox/')\n        else:\n            LOGERR('buku does not support {} yet'.format(sys.platform))\n            self.close_quit(1)\n            return  # clarifying execution interrupt for the linter\n\n        ff_bm_db_paths = get_firefox_db_paths(default_ff_folder, firefox_profile)\n\n        if self.chatty:\n            resp = input('Generate auto-tag (YYYYMonDD)? (y/n): ')\n            if resp == 'y':\n                newtag = gen_auto_tag()\n            else:\n                newtag = None\n            resp = input('Add parent folder names as tags? (y/n): ')\n        else:\n            newtag = None\n            resp = 'y'\n        add_parent_folder_as_tag = resp == 'y'\n\n        with self.lock:\n            resp = 'y'\n\n            chrome_based = {'Google Chrome': gc_bm_db_path, 'Chromium': cb_bm_db_path, 'Vivaldi': vi_bm_db_path,\n                            'Brave': br_bm_db_path}\n            for name, path in chrome_based.items():\n                try:\n                    if os.path.isfile(os.path.expanduser(path)):\n                        if self.chatty:\n                            resp = input(f'Import bookmarks from {name}? (y/n): ')\n                        if resp == 'y':\n                            bookmarks_database = os.path.expanduser(path)\n                            if not os.path.exists(bookmarks_database):\n                                raise FileNotFoundError\n                            self.load_chrome_database(bookmarks_database, newtag, add_parent_folder_as_tag)\n                except Exception as e:\n                    LOGERR(e)\n                    print(f'Could not import bookmarks from {name}')\n\n            try:\n                ff_bm_db_paths = {k: s for k, s in ff_bm_db_paths.items() if os.path.isfile(os.path.expanduser(s))}\n                for idx, (name, ff_bm_db_path) in enumerate(ff_bm_db_paths.items(), start=1):\n                    if self.chatty:\n                        profile = ('' if len(ff_bm_db_paths) < 2 else\n                                   f' profile {name} [{idx}/{len(ff_bm_db_paths)}]')\n                        resp = input(f'Import bookmarks from Firefox{profile}? (y/n): ')\n                    if resp == 'y':\n                        bookmarks_database = os.path.expanduser(ff_bm_db_path)\n                        if not os.path.exists(bookmarks_database):\n                            raise FileNotFoundError\n                        self.load_firefox_database(bookmarks_database, newtag, add_parent_folder_as_tag)\n                        break\n            except Exception as e:\n                LOGERR(e)\n                print('Could not import bookmarks from Firefox.')\n\n            try:\n                if os.path.isfile(os.path.expanduser(me_bm_db_path)):\n                    if self.chatty:\n                        resp = input('Import bookmarks from microsoft edge? (y/n): ')\n                    if resp == 'y':\n                        bookmarks_database = os.path.expanduser(me_bm_db_path)\n                        if not os.path.exists(bookmarks_database):\n                            raise FileNotFoundError\n                        self.load_edge_database(bookmarks_database, newtag, add_parent_folder_as_tag)\n            except Exception as e:\n                LOGERR(e)\n                print('Could not import bookmarks from microsoft-edge')\n\n            self.conn.commit()\n\n        if newtag:\n            print('\\nAuto-generated tag: %s' % newtag)\n\n    def importdb(self, filepath, tacit=False):\n        \"\"\"Import bookmarks from an HTML or a Markdown file.\n\n        Supports Firefox, Google Chrome, and IE exported HTML bookmarks.\n        Supports XBEL standard bookmarks.\n        Supports RSS files with extension '.rss', '.atom'.\n        Supports Markdown files with extension '.md', '.org'.\n        Supports importing bookmarks from another buku database file.\n\n        Parameters\n        ----------\n        filepath : str\n            Path to file to import.\n        tacit : bool\n            If True, no questions asked and folder names are automatically\n            imported as tags from bookmarks HTML.\n            If True, automatic timestamp tag is NOT added.\n            Default is False.\n\n        Returns\n        -------\n        bool\n            True on success, False on failure.\n        \"\"\"\n\n        if filepath.endswith('.db'):\n            return self.mergedb(filepath)\n\n        newtag = None\n        append_tags_resp = 'y'\n        if not tacit:\n            if input('Generate auto-tag (YYYYMonDD)? (y/n): ') == 'y':\n                newtag = gen_auto_tag()\n            append_tags_resp = input('Append tags when bookmark exist? (y/n): ')\n\n        items = []\n        if filepath.endswith('.md'):\n            items = import_md(filepath=filepath, newtag=newtag)\n        elif filepath.endswith('org'):\n            items = import_org(filepath=filepath, newtag=newtag)\n        elif filepath.endswith('rss') or filepath.endswith('atom'):\n            items = import_rss(filepath=filepath, newtag=newtag)\n        elif filepath.endswith('json'):\n            if not tacit:\n                resp = input('Add parent folder names as tags? (y/n): ')\n            else:\n                resp = 'y'\n            add_bookmark_folder_as_tag = resp == 'y'\n            try:\n                with open(filepath, 'r', encoding='utf-8') as datafile:\n                    data = json.load(datafile)\n\n                items = import_firefox_json(data, add_bookmark_folder_as_tag, newtag)\n            except ValueError as e:\n                LOGERR(\"ff_json: JSON Decode Error: {}\".format(e))\n                return False\n            except Exception as e:\n                LOGERR(e)\n                return False\n        elif filepath.endswith('xbel'):\n            try:\n                with open(filepath, mode='r', encoding='utf-8') as infp:\n                    soup = BeautifulSoup(infp, 'html.parser')\n            except ImportError:\n                LOGERR('Beautiful Soup not found')\n                return False\n            except Exception as e:\n                LOGERR(e)\n                return False\n\n            add_parent_folder_as_tag = False\n            use_nested_folder_structure = False\n            if not tacit:\n                resp = input(\"\"\"Add bookmark's parent folder as tag?\na: add all parent folders of the bookmark\nn: don't add parent folder as tag\n(a/[n]): \"\"\")\n            else:\n                resp = 'y'\n\n            if resp == 'a':\n                add_parent_folder_as_tag = True\n                use_nested_folder_structure = True\n\n            items = import_xbel(soup, add_parent_folder_as_tag, newtag, use_nested_folder_structure)\n            infp.close()\n        else:\n            try:\n                with open(filepath, mode='r', encoding='utf-8') as infp:\n                    soup = BeautifulSoup(infp, 'html.parser')\n            except ImportError:\n                LOGERR('Beautiful Soup not found')\n                return False\n            except Exception as e:\n                LOGERR(e)\n                return False\n\n            add_parent_folder_as_tag = False\n            use_nested_folder_structure = False\n            if not tacit:\n                resp = input(\"\"\"Add bookmark's parent folder as tag?\ny: add single, direct parent folder\na: add all parent folders of the bookmark\nn: don't add parent folder as tag\n(y/a/[n]): \"\"\")\n            else:\n                resp = 'y'\n\n            if resp in ('y', 'a'):\n                add_parent_folder_as_tag = True\n                if resp == 'a':\n                    use_nested_folder_structure = True\n\n            items = import_html(soup, add_parent_folder_as_tag, newtag, use_nested_folder_structure)\n            infp.close()\n\n        with self.lock:\n            for item in items:\n                add_rec_res = self.add_rec(*item)\n                if not add_rec_res and append_tags_resp == 'y':\n                    rec_id = self.get_rec_id(item[0])\n                    self.append_tag_at_index(rec_id, item[2])\n\n            self.conn.commit()\n\n        if newtag:\n            print('\\nAuto-generated tag: %s' % newtag)\n\n        return True\n\n    def mergedb(self, path):\n        \"\"\"Merge bookmarks from another buku database file.\n\n        Parameters\n        ----------\n        path : str\n            Path to DB file to merge.\n\n        Returns\n        -------\n        bool\n            True on success, False on failure.\n        \"\"\"\n\n        try:\n            # Connect to input DB\n            indb_conn = sqlite3.connect('file:%s?mode=ro' % path, uri=True)\n\n            indb_cur = indb_conn.cursor()\n            indb_cur.execute('SELECT * FROM bookmarks')\n        except Exception as e:\n            LOGERR(e)\n            return False\n\n        resultset = indb_cur.fetchall()\n        if resultset:\n            with self.lock:\n                for row in bookmark_vars(resultset):\n                    self.add_rec(row.url, row.title, row.tags_raw, row.desc, row.flags, True, False)\n\n                self.conn.commit()\n\n        try:\n            indb_cur.close()\n            indb_conn.close()\n        except Exception:\n            pass\n\n        return True\n\n    def tnyfy_url(\n            self,\n            index: Optional[int] = None,\n            url: Optional[str] = None,\n            shorten: bool = True) -> Optional[str]:\n        \"\"\"Shorten/expand a URL using the tny.im service.\n\n        Tny.im is no longer available; don't use this method.\n        \"\"\"\n        warn('\\'BukuDb.tnyfy_url()\\' no longer works due to the takedown of tny.im service.', DeprecationWarning)\n\n    def browse_cached_url(self, arg):\n        \"\"\"Open URL at index or URL.\n\n        Parameters\n        ----------\n        arg : str\n            Index or url to browse\n\n        Returns\n        -------\n        str\n            Wayback Machine URL, None if not cached\n        \"\"\"\n\n        from urllib.parse import quote_plus\n\n        if is_int(arg):\n            rec = self.get_rec_by_id(int(arg))\n            if not rec:\n                LOGERR('No matching index %d', int(arg))\n                return None\n            url = rec[1]\n        else:\n            url = arg\n\n        # Try fetching cached page from Wayback Machine\n        api_url = 'https://archive.org/wayback/available?url=' + quote_plus(url)\n        manager = get_PoolManager()\n        resp = manager.request('GET', api_url)\n        respobj = json.loads(resp.data)\n        try:\n            if (\n                    len(respobj['archived_snapshots']) and\n                    respobj['archived_snapshots']['closest']['available'] is True):\n                manager.clear()\n                return respobj['archived_snapshots']['closest']['url']\n        except Exception:\n            pass\n        finally:\n            manager.clear()\n\n        LOGERR('Uncached')\n        return None\n\n    def fixtags(self):\n        \"\"\"Undocumented API to fix tags set in earlier versions.\n\n        Functionalities:\n\n        1. Remove duplicate tags\n        2. Sort tags\n        3. Use lower case to store tags\n        \"\"\"\n\n        to_commit = False\n        with self.lock:\n            self.cur.execute('SELECT id, tags FROM bookmarks ORDER BY id ASC')\n            resultset = self.cur.fetchall()\n            query = 'UPDATE bookmarks SET tags = ? WHERE id = ?'\n            for row in resultset:\n                oldtags = row[1]\n                if oldtags == DELIM:\n                    continue\n\n                tags = parse_tags([oldtags])\n                if tags == oldtags:\n                    continue\n\n                self.cur.execute(query, (tags, row[0],))\n                to_commit = True\n\n            if to_commit:\n                self.conn.commit()\n\n    def close(self):\n        \"\"\"Close a DB connection.\"\"\"\n\n        if self.conn is not None:\n            try:\n                self.cur.close()\n                self.conn.close()\n            except Exception:\n                # ignore errors here, we're closing down\n                pass\n\n    def close_quit(self, exitval=0):\n        \"\"\"Close a DB connection and exit.\n\n        Parameters\n        ----------\n        exitval : int\n            Program exit value.\n        \"\"\"\n\n        if self.conn is not None:\n            try:\n                self.cur.close()\n                self.conn.close()\n            except Exception:\n                # ignore errors here, we're closing down\n                pass\n        sys.exit(exitval)\n\n\nclass ExtendedArgumentParser(argparse.ArgumentParser):\n    \"\"\"Extend classic argument parser.\"\"\"\n\n    def __init__(self, *args, **kwargs):\n        self._nodefaults, self._unset = None, object()\n        super().__init__(*args, **kwargs)\n        self._nodefaults = argparse.ArgumentParser(*args, **kwargs)\n\n    def _add_argument(self, old_add_arg, nodefaults, *args, **kwargs):\n        old_add_arg(*args, **kwargs)\n        kwargs = dict(kwargs)\n        kwargs.pop('type', None)\n        kwargs.pop('choices', None)\n        kwargs['default'] = self._unset\n        nodefaults and nodefaults.add_argument(*args, **kwargs)\n\n    def add_argument(self, *args, **kwargs):\n        self._add_argument(super().add_argument, self._nodefaults, *args, **kwargs)\n\n    def add_argument_group(self, *args, **kwargs):\n        group = super().add_argument_group(*args, **kwargs)\n        nodefaults = self._nodefaults and self._nodefaults.add_argument_group(*args, **kwargs)\n        old_add_arg = group.add_argument\n        group.add_argument = lambda *a, **kw: self._add_argument(old_add_arg, nodefaults, *a, **kw)\n        return group\n\n    def parse_args(self, *args, **kwargs):\n        result = super().parse_args(*args, **kwargs)\n        nodefaults = self._nodefaults.parse_args(*args, **kwargs)\n        params = {k for k in dir(nodefaults) if not k.startswith('_')}\n        setattr(result, '_passed', {k for k in params if getattr(nodefaults, k) != self._unset})\n        return result\n\n    @staticmethod\n    def program_info(file=sys.stdout):\n        \"\"\"Print program info.\n\n        Parameters\n        ----------\n        file : file\n            File to write program info to. Default is sys.stdout.\n        \"\"\"\n        if sys.platform == 'win32' and file == sys.stdout:\n            file = sys.stderr\n\n        file.write('''\nSYMBOLS:\n      >                    url\n      +                    comment\n      #                    tags\n\nVersion %s\nCopyright © 2015-2026 %s\nLicense: %s\nWebpage: https://github.com/jarun/buku\n''' % (__version__, __author__, __license__))\n\n    @staticmethod\n    def prompt_help(file=sys.stdout):\n        \"\"\"Print prompt help.\n\n        Parameters\n        ----------\n        file : file\n            File to write program info to. Default is sys.stdout.\n        \"\"\"\n        file.write('''\nPROMPT KEYS:\n    1-N                    browse search result indices and/or ranges\n    R [N]                  print out N random search results\n                           (or random bookmarks if negative or N/A)\n    ^ id1 id2              swap two records at specified indices\n    O [id|range [...]]     open search results/indices in GUI browser\n                           toggle try GUI browser if no arguments\n    a                      open all results in browser\n    s keyword [...]        search for records with ANY keyword\n    S keyword [...]        search for records with ALL keywords\n    d                      match substrings ('pen' matches 'opened')\n    m                      search with markers - search string is split\n                           into keywords by prefix markers, which determine\n                           what field the keywords is searched in:\n                           '.', '>' or ':' - title, description or URL\n                           '#'/'#,' - tags (comma-separated, partial/full match)\n                           '*' - all fields (can be omitted in the 1st keyword)\n                           note: tag marker is not affected by 'd' (deep search)\n    v fields               change sorting order (default is '+index')\n                           multiple comma/space separated fields can be specified\n    v! fields              update indices in DB to match specified order\n    r expression           run a regex search\n    t [tag, ...]           search by tags; show taglist, if no args\n    g taglist id|range [...] [>>|>|<<] [record id|range ...]\n                           append, set, remove (all or specific) tags\n                           search by taglist id(s) if records are omitted\n    n                      show next page of search results\n    N                      show previous page of search results\n    o id|range [...]       browse bookmarks by indices and/or ranges\n    p id|range [...]       print bookmarks by indices and/or ranges\n    w [editor|id]          edit and add or update a bookmark\n    c id                   copy url at search result index to clipboard\n    DB [name]              check existing DB list or switch to another DB\n                           (use full/dir path to switch folders)\n                           '~.' can be used as shortcut for default DB\n    ?                      show this help\n    q, ^D, double Enter    exit buku\n\n''')\n\n    @staticmethod\n    def is_colorstr(arg):\n        \"\"\"Check if a string is a valid color string.\n\n        Parameters\n        ----------\n        arg : str\n            Color string to validate.\n\n        Returns\n        -------\n        str\n            Same color string that was passed as an argument.\n\n        Raises\n        ------\n        ArgumentTypeError\n            If the arg is not a valid color string.\n        \"\"\"\n        try:\n            assert len(arg) == 5\n            for c in arg:\n                assert c in COLORMAP\n        except AssertionError as e:\n            raise argparse.ArgumentTypeError('%s is not a valid color string' % arg) from e\n        return arg\n\n    # Help\n    def print_help(self, file=sys.stdout):\n        \"\"\"Print help prompt.\n\n        Parameters\n        ----------\n        file : file\n            File to write program info to. Default is sys.stdout.\n        \"\"\"\n        super().print_help(file)\n        self.program_info(file)\n\n\n# ----------------\n# Helper functions\n# ----------------\n\n\nConverterResult = TypedDict('ConverterResult', {'data': str, 'count': int}) if TypedDict else Dict[str, Any]\n\n\ndef convert_tags_to_org_mode_tags(tags: str) -> str:\n    \"\"\"convert buku tags to org-mode compatible tags.\"\"\"\n    if tags != DELIM:\n        buku_tags = tags.split(DELIM)[1:-1]\n        buku_tags = [re.sub(r'[^a-zA-Z0-9_@]', ' ', tag) for tag in buku_tags]\n        buku_tags = [re.sub(r'\\s+', ' ', tag) for tag in buku_tags]\n        buku_tags = taglist(x.replace(' ', '_') for x in buku_tags)\n        if buku_tags:\n            return ' :{}:\\n'.format(':'.join(buku_tags))\n    return '\\n'\n\n\ndef convert_bookmark_set(\n        bookmark_set: List[BookmarkVar],\n        export_type: str,\n        old: Optional[Dict[str, str | BookmarkVar]] = None) -> ConverterResult:  # type: ignore\n    \"\"\"Convert list of bookmark set into multiple data format.\n\n    Parameters\n    ----------\n        bookmark_set: bookmark set\n        export_type: one of supported type: markdown, html, org, XBEL\n        old: cached values of deleted records/replaced URLs to save\n\n    Returns\n    -------\n        converted data and count of converted bookmark set\n    \"\"\"\n    import html\n    assert export_type in ['markdown', 'html', 'org', 'xbel', 'rss']\n    #  compatibility\n    resultset = bookmark_vars(bookmark_set)\n    old = old or {}\n\n    def title(row):\n        _old = old.get(row.url)\n        _add = (f' (OLD URL = {_old})' if isinstance(_old, str) and _old != row.url else\n                ' (DELETED)' if _old == row else '')\n        return (row.title or '') + _add\n\n    count = 0\n    out = ''\n    if export_type == 'markdown':\n        for row in resultset:\n            _title = title(row)\n            out += (f'- <{row.url}>' if not _title else f'- [{_title}]({row.url})')\n\n            if row.tags:\n                out += ' <!-- TAGS: {} -->\\n'.format(row.tags)\n            else:\n                out += '\\n'\n\n            count += 1\n    elif export_type == 'org':\n        for row in resultset:\n            _title = title(row)\n            out += (f'* [[{row.url}]]' if not _title else f'* [[{row.url}][{_title}]]')\n            out += convert_tags_to_org_mode_tags(row.tags_raw)\n            count += 1\n    elif export_type == 'xbel':\n        timestamp = str(int(time.time()))\n        out = (\n            '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n'\n            '<!DOCTYPE xbel PUBLIC \\\n\"+//IDN python.org//DTD XML Bookmark Exchange Language 1.0//EN//XML\" \\\n\"http://pyxml.sourceforge.net/topics/dtds/xbel.dtd\">\\n\\n'\n            '<xbel version=\"1.0\">\\n')\n\n        for row in resultset:\n            out += '    <bookmark href=\"%s\"' % (html.escape(row.url)).encode('ascii', 'xmlcharrefreplace').decode('utf-8')\n            if row.tags:\n                out += ' TAGS=\"' + html.escape(row.tags).encode('ascii', 'xmlcharrefreplace').decode('utf-8') + '\"'\n            out += '>\\n        <title>{}</title>'\\\n                .format(html.escape(title(row)).encode('ascii', 'xmlcharrefreplace').decode('utf-8'))\n            if row.desc:\n                out += '\\n        <desc>{}</desc>'.format(html.escape(row.desc).encode('ascii', 'xmlcharrefreplace').decode('utf-8'))\n            out += '\\n    </bookmark>\\n'\n            count += 1\n\n        out += '</xbel>'\n    elif export_type == 'rss':\n        out = (\n            '<feed xmlns=\"http://www.w3.org/2005/Atom\">\\n'\n            '    <title>Bookmarks</title>\\n'\n            '    <generator uri=\"https://github.com/jarun/buku\">buku</generator>\\n'\n        )\n\n        for row in resultset:\n            out += '    <entry>\\n'\n            out += '        <title>' + title(row) + '</title>\\n'\n            _url = html.escape(row.url).encode('ascii', 'xmlcharrefreplace').decode('utf-8')\n            out += '        <link href=\"%s\" rel=\"alternate\" type=\"text/html\"/>\\n' % _url\n            out += '        <id>%s</id>\\n' % row.id\n            for tag in (t for t in row.tags.split(',') if t):\n                _tag = html.escape(tag).encode('ascii', 'xmlcharrefreplace').decode('utf-8')\n                out += '        <category term=\"%s\"/>\\n' % _tag\n            if row.desc:\n                _desc = html.escape(row.desc).encode('ascii', 'xmlcharrefreplace').decode('utf-8')\n                out += '        <content type=\"html\"> <![CDATA[ <p>%s</p> ]]> </content>\\n' % _desc\n            out += '    </entry>\\n'\n            count += 1\n\n        out += '</feed>'\n    elif export_type == 'html':\n        timestamp = str(int(time.time()))\n        out = (\n            '<!DOCTYPE NETSCAPE-Bookmark-file-1>\\n\\n'\n            '<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\">\\n'\n            '<TITLE>Bookmarks</TITLE>\\n'\n            '<H1>Bookmarks</H1>\\n\\n'\n            '<DL><p>\\n'\n            '    <DT><H3 ADD_DATE=\"{0}\" LAST_MODIFIED=\"{0}\" '\n            'PERSONAL_TOOLBAR_FOLDER=\"true\">buku bookmarks</H3>\\n'\n            '    <DL><p>\\n'.format(timestamp))\n\n        for row in resultset:\n            out += '        <DT><A HREF=\"%s\" ADD_DATE=\"%s\" LAST_MODIFIED=\"%s\"' % (row.url, timestamp, timestamp)\n            if row.tags:\n                out += ' TAGS=\"' + row.tags + '\"'\n            out += '>{}</A>\\n'.format(title(row))\n            if row.desc:\n                out += '        <DD>' + row.desc + '\\n'\n            count += 1\n\n        out += '    </DL><p>\\n</DL><p>'\n\n    return {'data': out, 'count': count}\n\n\ndef get_firefox_profile_names(path):\n    \"\"\"List folder and detect default Firefox profile names for all installs.\n\n    Returns\n    -------\n    profiles : [str]\n        All default Firefox profile names.\n    \"\"\"\n    from configparser import ConfigParser, NoOptionError\n\n    profiles = []\n    profile_path = os.path.expanduser(os.path.join(path, 'profiles.ini'))\n    if os.path.exists(profile_path):\n        config = ConfigParser()\n        config.read(profile_path)\n\n        install_names = [section for section in config.sections() if section.startswith('Install')]\n        for name in install_names:\n            try:\n                profiles += [config.get(name, 'default')]\n            except NoOptionError:\n                pass\n        if profiles:\n            return profiles\n\n        profiles_names = [section for section in config.sections() if section.startswith('Profile')]\n        for name in profiles_names:\n            try:\n                # If profile is default\n                if config.getboolean(name, 'default'):\n                    profiles += [config.get(name, 'path')]\n                    continue\n            except NoOptionError:\n                pass\n            try:\n                # alternative way to detect default profile\n                if config.get(name, 'name').lower() == \"default\":\n                    profiles += [config.get(name, 'path')]\n            except NoOptionError:\n                pass\n\n        return profiles\n\n    # There are no default profiles\n    LOGDBG('get_firefox_profile_names(): {} does not exist'.format(path))\n    return profiles\n\ndef get_firefox_db_paths(default_ff_folder, specified=None):\n    profiles = ([specified] if specified else get_firefox_profile_names(default_ff_folder))\n    _profile_path = lambda s: (s if os.path.isabs(s) else os.path.join(default_ff_folder, s))\n    return {s: os.path.join(_profile_path(s), 'places.sqlite') for s in profiles}\n\n\ndef walk(root):\n    \"\"\"Recursively iterate over JSON.\n\n    Parameters\n    ----------\n    root : JSON element\n        Base node of the JSON data.\n    \"\"\"\n\n    for element in root['children']:\n        if element['type'] == 'url':\n            url = element['url']\n            title = element['name']\n            yield (url, title, None, None, 0, True)\n        else:\n            walk(element)\n\n\ndef import_md(filepath: str, newtag: Optional[str]):\n    \"\"\"Parse bookmark Markdown file.\n\n    Parameters\n    ----------\n    filepath : str\n        Path to Markdown file.\n    newtag : str, optional\n        New tag for bookmarks in Markdown file.\n\n    Returns\n    -------\n    tuple\n        Parsed result.\n    \"\"\"\n    # Supported Markdown format: `[title](url) <!-- TAGS: tags -->` (or `<url> <!-- TAGS: tags -->`)\n    _named_link, _raw_link = r'\\[(?P<title>.*)\\]\\((?P<url>.+)\\)', r'\\<(?P<url_raw>[^!>][^>]*)\\>'\n    pattern = re.compile(r'(%s|%s)(\\s+<!-- TAGS: (?P<tags>.*) -->)?' % (_named_link, _raw_link))\n    with open(filepath, mode='r', encoding='utf-8') as infp:\n        for line in infp:\n            if match := pattern.search(line):\n                title = match.group('title') or ''\n                url = match.group('url') or match.group('url_raw')\n\n                if is_nongeneric_url(url):\n                    continue\n\n                tags = DELIM.join(s for s in [newtag, match.group('tags')] if s)\n                tags = parse_tags([tags])\n\n                yield (url, title, delim_wrap(tags), None, 0, True, False)\n\ndef import_rss(filepath: str, newtag: Optional[str]):\n    \"\"\"Parse bookmark RSS file.\n\n    Parameters\n    ----------\n    filepath : str\n        Path to RSS file.\n    newtag : str, optional\n        New tag for bookmarks in RSS file.\n\n    Returns\n    tuple\n        Parsed result.\n    \"\"\"\n\n    with open(filepath, mode='r', encoding='utf-8') as infp:\n        ns = {'atom': 'http://www.w3.org/2005/Atom'}\n        root = ET.fromstring(infp.read())\n        for entry in root.findall('atom:entry', ns):\n            title = entry.find('atom:title', ns).text\n            url = entry.find('atom:link', ns).attrib['href']\n            tags = ','.join([tag.attrib['term'] for tag in entry.findall('atom:category', ns)])\n            if newtag is not None:\n                tags = newtag + ',' + tags\n            desc = entry.find('atom:content', ns)\n            desc = desc.text if desc is not None else None\n            yield (url, title, delim_wrap(tags), desc, 0, True, False)\n\ndef import_org(filepath: str, newtag: Optional[str]):\n    \"\"\"Parse bookmark org file.\n\n    Parameters\n    ----------\n    filepath : str\n        Path to org file.\n    newtag : str, optional\n        New tag for bookmarks in org file.\n\n    Returns\n    -------\n    tuple\n        Parsed result.\n    \"\"\"\n    def get_org_tags(tag_string):\n        \"\"\"Extracts tags from Org\n\n        Parameters\n        ----------\n        tag_string: str\n             string of tags in Org-format\n\n        Syntax: Org splits tags with colons. If colons are part of a buku-tag, this is indicated by using\n                multiple colons in org. If a buku-tag starts or ends with a colon, this is indicated by a\n                preceding or trailing whitespace\n\n        Returns\n        -------\n        list\n            List of tags\n        \"\"\"\n        tag_list_raw = [s for s in re.split(r'(?<!\\:)\\:', tag_string) if s]\n        tag_list_cleaned = []\n        for i, tag in enumerate(tag_list_raw):\n            if tag.startswith(\":\"):\n                if tag_list_raw[i-1] == ' ':\n                    tag_list_cleaned.append(tag.strip())\n                else:\n                    new_item = tag_list_cleaned[-1] + tag\n                    del tag_list_cleaned[-1]\n                    tag_list_cleaned.append(new_item.strip())\n            elif tag != ' ':\n                tag_list_cleaned.append(tag.strip())\n        return tag_list_cleaned\n\n    # Supported OrgMode format: `[[url][title]] :tags:` (or `[[url]] :tags:`)\n    _url, _maybe_title = r'(?P<url>((?!\\]\\[).)+?)', r'(\\]\\[(?P<title>.+))?'\n    pattern = re.compile(r'\\[\\[%s%s\\]\\](?P<tags>\\s+:.*:)?' % (_url, _maybe_title))\n    with open(filepath, mode='r', encoding='utf-8') as infp:\n        for line in infp:\n            if match := pattern.search(line):\n                title = match.group('title') or ''\n                url = match.group('url')\n\n                if is_nongeneric_url(url):\n                    continue\n\n                tags = list(dict.fromkeys(get_org_tags(match.group('tags') or '')))\n                tags_string = DELIM.join(tags)\n                if newtag and newtag.lower() not in tags:\n                    tags_string = (newtag + DELIM) + tags_string\n\n                yield (url, title, delim_wrap(tags_string), None, 0, True, False)\n\ndef import_firefox_json(json, add_bookmark_folder_as_tag=False, unique_tag=None):\n    \"\"\"Open Firefox JSON export file and import data.\n    Ignore 'SmartBookmark'  and 'Separator'  entries.\n\n    Needed/used fields out of the JSON schema of the bookmarks:\n\n    title              : the name/title of the entry\n    tags               : ',' separated tags for the bookmark entry\n    typeCode           : 1 - uri, 2 - subfolder, 3 - separator\n    annos/{name,value} : following annotation entries are used\n        name : Places/SmartBookmark            : identifies smart folder, ignored\n        name : bookmarkPropereties/description :  detailed bookmark entry description\n    children           : for subfolders, recurse into the child entries\n\n    Parameters\n    ----------\n    path : str\n        Path to Firefox JSON bookmarks file.\n    unique_tag : str\n        Timestamp tag in YYYYMonDD format.\n    add_bookmark_folder_as_tag : bool\n        True if bookmark parent folder should be added as tags else False.\n    \"\"\"\n\n    class TypeCode(Enum):\n        \"\"\" Format\n            typeCode\n                1 : uri        (type=text/x-moz-place)\n                2 : subfolder  (type=text/x-moz-container)\n                3 : separator  (type=text/x-moz-separator)\n        \"\"\"\n        uri = 1\n        folder = 2\n        separator = 3\n\n    def is_smart(entry):\n        result = False\n        try:\n            d = [anno for anno in entry['annos'] if anno['name'] == \"Places/SmartBookmark\"]\n            result = bool(len(d))\n        except Exception:\n            result = False\n\n        return result\n\n    def extract_desc(entry):\n        try:\n            d = [\n                anno for anno in entry['annos']\n                if anno['name'] == \"bookmarkProperties/description\"\n            ]\n            return d[0]['value']\n        except Exception:\n            LOGDBG(\"ff_json: No description found for entry: {} {}\".format(entry['uri'], entry['title']))\n            return \"\"\n\n    def extract_tags(entry):\n        tags = []\n        try:\n            tags = entry['tags'].split(',')\n        except Exception:\n            LOGDBG(\"ff_json: No tags found for entry: {} {}\".format(entry['uri'], entry['title']))\n\n        return tags\n\n    def iterate_children(parent_folder, entry_list):\n        for bm_entry in entry_list:\n            entry_title = bm_entry['title'] if 'title' in bm_entry else \"<no title>\"\n\n            try:\n                typeCode = bm_entry['typeCode']\n            except Exception:\n                LOGDBG(\"ff_json: item without typeCode found, ignoring: {}\".format(entry_title))\n                continue\n\n            LOGDBG(\"ff_json: processing typeCode '{}', title '{}'\".format(typeCode, entry_title))\n            if TypeCode.uri.value == typeCode:\n                try:\n                    if is_smart(bm_entry):\n                        LOGDBG(\"ff_json: SmartBookmark found, ignoring: {}\".format(entry_title))\n                        continue\n\n                    if is_nongeneric_url(bm_entry['uri']):\n                        LOGDBG(\"ff_json: Non-Generic URL found, ignoring: {}\".format(entry_title))\n                        continue\n\n                    desc = extract_desc(bm_entry)\n                    bookmark_tags = extract_tags(bm_entry)\n\n                    # if parent_folder is not \"None\"\n                    if add_bookmark_folder_as_tag and parent_folder:\n                        bookmark_tags.append(parent_folder)\n\n                    if unique_tag:\n                        bookmark_tags.append(unique_tag)\n\n                    formatted_tags = [DELIM + tag for tag in bookmark_tags]\n                    tags = parse_tags(formatted_tags)\n\n                    LOGDBG(\"ff_json: Entry found: {}, {}, {}, {} \" .format(bm_entry['uri'], entry_title, tags, desc))\n                    yield (bm_entry['uri'], entry_title, tags, desc, 0, True, False)\n\n                except Exception as e:\n                    LOGERR(\"ff_json: Error parsing entry '{}' Exception '{}'\".format(entry_title, e))\n\n            elif TypeCode.folder.value == typeCode:\n\n                # ignore special bookmark folders\n                if 'root' in bm_entry and bm_entry['root'] in IGNORE_FF_BOOKMARK_FOLDERS:\n                    LOGDBG(\"ff_json: ignoring root folder: {}\" .format(entry_title))\n                    entry_title = None\n\n                if \"children\" in bm_entry:\n                    yield from iterate_children(entry_title, bm_entry['children'])\n                else:\n                    # if any of the properties does not exist, bail out silently\n                    LOGDBG(\"ff_json: No 'children' found in bookmark folder - skipping: {}\".format(entry_title))\n\n            elif TypeCode.separator.value == typeCode:\n                # ignore separator\n                pass\n            else:\n                LOGDBG(\"ff_json: Unknown typeCode found : {}\".format(typeCode))\n\n    if \"children\" in json:\n        main_entry_list = json['children']\n    else:\n        LOGDBG(\"ff_json: No children in Root entry found\")\n        return []\n\n    yield from iterate_children(None, main_entry_list)\n\ndef import_xbel(html_soup: BeautifulSoup, add_parent_folder_as_tag: bool, newtag: str, use_nested_folder_structure: bool = False):\n    \"\"\"Parse bookmark XBEL.\n\n    Parameters\n    ----------\n    html_soup : BeautifulSoup object\n        BeautifulSoup representation of bookmark HTML.\n    add_parent_folder_as_tag : bool\n        True if bookmark parent folders should be added as tags else False.\n    newtag : str\n        A new unique tag to add to imported bookmarks.\n    use_nested_folder_structure: bool\n        True if all bookmark parent folder should be added, not just direct parent else False\n        add_parent_folder_as_tag must be True for this flag to have an effect\n\n    Returns\n    -------\n    tuple\n        Parsed result.\n    \"\"\"\n\n    # compatibility\n    soup = html_soup\n\n    for tag in soup.find_all('bookmark'):\n        # Extract comment from <desc> tag\n        try:\n            if is_nongeneric_url(tag['href']):\n                continue\n        except KeyError:\n            continue\n\n        title_tag = tag.title.string\n\n        desc = None\n        comment_tag = tag.find_next_sibling('desc')\n\n        if comment_tag:\n            desc = comment_tag.find(text=True, recursive=False)\n\n        if add_parent_folder_as_tag:\n            # add parent folder as tag\n            if use_nested_folder_structure:\n                # New method that would generalize for else case to\n                # structure of folders\n                # folder\n                #   title (folder name)\n                #   folder\n                #       title\n                #           bookmark (could be h3, and continue recursively)\n                parents = tag.find_parents('folder')\n                for parent in parents:\n                    header = parent.find_previous_sibling('title')\n                    if header:\n                        if tag.has_attr('tags'):\n                            tag['tags'] += (DELIM + header.text)\n                        else:\n                            tag['tags'] = header.text\n            else:\n                # could be its folder or not\n                possible_folder = tag.find_previous('title')\n                # get list of tags within that folder\n                tag_list = tag.parent.parent.find_parent('folder')\n\n                if ((possible_folder) and possible_folder.parent in list(tag_list.parents)):\n                    # then it's the folder of this bookmark\n                    if tag.has_attr('tags'):\n                        tag['tags'] += (DELIM + possible_folder.text)\n                    else:\n                        tag['tags'] = possible_folder.text\n\n        # add unique tag if opted\n        if newtag:\n            if tag.has_attr('tags'):\n                tag['tags'] += (DELIM + newtag)\n            else:\n                tag['tags'] = newtag\n\n        yield (\n            tag['href'], title_tag,\n            parse_tags([tag['tags']]) if tag.has_attr('tags') else None,\n            desc if not desc else desc.strip(), 0, True, False\n        )\n\ndef import_html(html_soup: BeautifulSoup, add_parent_folder_as_tag: bool, newtag: str, use_nested_folder_structure: bool = False):\n    \"\"\"Parse bookmark HTML.\n\n    Parameters\n    ----------\n    html_soup : BeautifulSoup object\n        BeautifulSoup representation of bookmark HTML.\n    add_parent_folder_as_tag : bool\n        True if bookmark parent folders should be added as tags else False.\n    newtag : str\n        A new unique tag to add to imported bookmarks.\n    use_nested_folder_structure: bool\n        True if all bookmark parent folder should be added, not just direct parent else False\n        add_parent_folder_as_tag must be True for this flag to have an effect\n\n    Returns\n    -------\n    tuple\n        Parsed result.\n    \"\"\"\n\n    # compatibility\n    soup = html_soup\n\n    for tag in soup.find_all('a'):\n        # Extract comment from <dd> tag\n        try:\n            if is_nongeneric_url(tag['href']):\n                continue\n        except KeyError:\n            continue\n\n        desc = None\n        comment_tag = tag.find_next_sibling('dd')\n\n        if comment_tag:\n            desc = comment_tag.find(string=True, recursive=False)\n\n        if add_parent_folder_as_tag:\n            # New method that would generalize for else case to\n            # structure of folders\n            # dt\n            #   h3 (folder name)\n            #   dl\n            #       dt\n            #           a (could be h3, and continue recursively)\n            parents = tag.find_parents('dl')\n            for parent in (parents if use_nested_folder_structure else parents[:1]):\n                header = parent.find_previous_sibling('h3')\n                if header:\n                    if tag.has_attr('tags'):\n                        tag['tags'] += (DELIM + strip_delim(header.text))\n                    else:\n                        tag['tags'] = strip_delim(header.text)\n\n        # add unique tag if opted\n        if newtag:\n            if tag.has_attr('tags'):\n                tag['tags'] += (DELIM + strip_delim(newtag))\n            else:\n                tag['tags'] = strip_delim(newtag)\n\n        yield (\n            tag['href'], tag.string,\n            parse_tags([tag['tags']]) if tag.has_attr('tags') else None,\n            desc if not desc else desc.strip(), 0, True, False\n        )\n\n\ndef get_netloc(url):\n    \"\"\"Get the netloc token, or None.\"\"\"\n\n    try:\n        netloc = urlparse(url).netloc\n        if not netloc and not urlparse(url).scheme:\n            # Try to prepend '//' and get netloc\n            netloc = urlparse('//' + url).netloc\n        return netloc or None\n    except Exception as e:\n        LOGERR('%s, URL: %s', e, url)\n        return None\n\n\ndef is_bad_url(url):\n    \"\"\"Check if URL is malformed.\n\n    .. Note:: This API is not bulletproof but works in most cases.\n\n    Parameters\n    ----------\n    url : str\n        URL to scan.\n\n    Returns\n    -------\n    bool\n        True if URL is malformed, False otherwise.\n    \"\"\"\n\n    netloc = get_netloc(url)\n    if not netloc:\n        return True\n\n    LOGDBG('netloc: %s', netloc)\n\n    # netloc cannot start or end with a '.'\n    if netloc.startswith('.') or netloc.endswith('.'):\n        return True\n\n    # netloc should have at least one '.'\n    return '.' not in netloc\n\n\ndef is_nongeneric_url(url):\n    \"\"\"Returns True for URLs which are non-http and non-generic.\n\n    Parameters\n    ----------\n    url : str\n        URL to scan.\n\n    Returns\n    -------\n    bool\n        True if URL is a non-generic URL, False otherwise.\n    \"\"\"\n\n    ignored_prefix = [\n        'about:',\n        'apt:',\n        'chrome://',\n        'file://',\n        'place:',\n        'vivaldi://',\n    ]\n\n    for prefix in ignored_prefix:\n        if url.startswith(prefix):\n            return True\n\n    return False\n\n\ndef is_ignored_mime(url):\n    \"\"\"Check if URL links to ignored MIME.\n\n    .. Note:: Only a 'HEAD' request is made for these URLs.\n\n    Parameters\n    ----------\n    url : str\n        URL to scan.\n\n    Returns\n    -------\n    bool\n        True if URL links to ignored MIME, False otherwise.\n    \"\"\"\n\n    for mime in SKIP_MIMES:\n        if url.lower().endswith(mime):\n            LOGDBG('matched MIME: %s', mime)\n            return True\n\n    return False\n\n\ndef is_unusual_tag(tagstr):\n    \"\"\"Identify unusual tags with word to comma ratio > 3.\n\n    Parameters\n    ----------\n    tagstr : str\n        tag string to check.\n\n    Returns\n    -------\n    bool\n        True if valid tag else False.\n    \"\"\"\n\n    if not tagstr:\n        return False\n\n    nwords = len(tagstr.split())\n    ncommas = tagstr.count(',') + 1\n\n    if nwords / ncommas > 3:\n        return True\n\n    return False\n\n\ndef parse_decoded_page(page):\n    \"\"\"Fetch title, description and keywords from decoded HTML page.\n\n    Parameters\n    ----------\n    page : str\n        Decoded HTML page.\n\n    Returns\n    -------\n    tuple\n        (title, description, keywords).\n    \"\"\"\n\n    title = ''\n    desc = ''\n    keys = ''\n\n    soup = BeautifulSoup(page, 'html5lib')\n\n    try:\n        title = soup.find('title').text.strip().replace('\\n', ' ')\n        if title:\n            title = re.sub(r'\\s{2,}', ' ', title)\n    except Exception as e:\n        LOGDBG(e)\n\n    description = (soup.find('meta', attrs={'name':'description'}) or\n                   soup.find('meta', attrs={'name':'Description'}) or\n                   soup.find('meta', attrs={'property':'description'}) or\n                   soup.find('meta', attrs={'property':'Description'}) or\n                   soup.find('meta', attrs={'name':'og:description'}) or\n                   soup.find('meta', attrs={'name':'og:Description'}) or\n                   soup.find('meta', attrs={'property':'og:description'}) or\n                   soup.find('meta', attrs={'property':'og:Description'}))\n    try:\n        if description:\n            desc = description.get('content').strip()\n            if desc:\n                desc = re.sub(r'\\s{2,}', ' ', desc)\n    except Exception as e:\n        LOGDBG(e)\n\n    keywords = (soup.find('meta', attrs={'name':'keywords'}) or\n                soup.find('meta', attrs={'name':'Keywords'}))\n    try:\n        if keywords:\n            keys = keywords.get('content').strip().replace('\\n', ' ')\n            keys = re.sub(r'\\s{2,}', ' ', re.sub(r'\\s*,\\s*', ',', keys))\n            if is_unusual_tag(keys):\n                if keys not in (title, desc):\n                    LOGDBG('keywords to description: %s', keys)\n                    if desc:\n                        desc = desc + '\\n## ' + keys\n                    else:\n                        desc = '* ' + keys\n\n                keys = ''\n    except Exception as e:\n        LOGDBG(e)\n\n    LOGDBG('title: %s', title)\n    LOGDBG('desc : %s', desc)\n    LOGDBG('keys : %s', keys)\n\n    return (title, desc, keys and keys.strip(DELIM))\n\n\ndef get_data_from_page(resp):\n    \"\"\"Detect HTTP response encoding and invoke parser with decoded data.\n\n    Parameters\n    ----------\n    resp : HTTP response\n        Response from GET request.\n\n    Returns\n    -------\n    tuple\n        (title, description, keywords).\n    \"\"\"\n\n    try:\n        charset = EncodingDetector.find_declared_encoding(resp.data, is_html=True)\n\n        if not charset and 'content-type' in resp.headers:\n            m = email.message.Message()\n            m['content-type'] = resp.headers['content-type']\n            if m.get_param('charset') is not None:\n                charset = m.get_param('charset')\n\n        if charset:\n            LOGDBG('charset: %s', charset)\n            title, desc, keywords = parse_decoded_page(resp.data.decode(charset, errors='replace'))\n        else:\n            title, desc, keywords = parse_decoded_page(resp.data.decode(errors='replace'))\n\n        return (title, desc, keywords)\n    except Exception as e:\n        LOGERR(e)\n        return (None, None, None)\n\n\ndef extract_auth(url):\n    \"\"\"Convert an url into an (auth, url) tuple [the returned URL will contain no auth part].\"\"\"\n    _url = urlparse(url)\n    if _url.username is None:  # no '@' in netloc\n        return None, url\n    auth = _url.username + ('' if _url.password is None else f':{_url.password}')\n    return auth, url.replace(auth + '@', '')\n\ndef gen_headers():\n    \"\"\"Generate headers for network connection.\"\"\"\n\n    global MYHEADERS, MYPROXY\n\n    MYHEADERS = {\n        'Accept-Encoding': 'gzip,deflate',\n        'User-Agent': USER_AGENT,\n        'Sec-Fetch-Mode': 'navigate',\n        'Accept': '*/*',\n        'Cookie': '',\n        'DNT': '1'\n    }\n\n    MYPROXY = os.environ.get('https_proxy')\n    if MYPROXY:\n        try:\n            auth, MYPROXY = extract_auth(MYPROXY)\n        except Exception as e:\n            LOGERR(e)\n            return\n\n        # Strip username and password (if present) and update headers\n        if auth:\n            auth_headers = make_headers(basic_auth=auth)\n            MYHEADERS.update(auth_headers)\n\n        LOGDBG('proxy: [%s]', MYPROXY)\n\n\ndef get_PoolManager():\n    \"\"\"Creates a pool manager with proxy support, if applicable.\n\n    Returns\n    -------\n    ProxyManager or PoolManager\n        ProxyManager if https_proxy is defined, PoolManager otherwise.\n    \"\"\"\n    ca_certs = os.getenv('BUKU_CA_CERTS', default=CA_CERTS)\n    if MYPROXY:\n        return urllib3.ProxyManager(MYPROXY, num_pools=1, headers=MYHEADERS, timeout=15,\n                                    cert_reqs='CERT_REQUIRED', ca_certs=ca_certs)\n\n    return urllib3.PoolManager(\n        num_pools=1,\n        headers=MYHEADERS,\n        timeout=15,\n        cert_reqs='CERT_REQUIRED',\n        ca_certs=ca_certs)\n\n\ndef network_handler(\n        url: str,\n        http_head: bool = False\n) -> Tuple[str, str, str, int, int]:\n    \"\"\"Handle server connection and redirections.\n\n    Deprecated; use fetch_data() instead.\n\n    Returns\n    -------\n    tuple\n        (title, description, tags, recognized mime, bad url)\n    \"\"\"\n    warn('\\'buku.network_handler()\\' is deprecated; use \\'buku.fetch_data()\\' instead.', DeprecationWarning)\n    result = fetch_data(url, http_head)\n    return (result.title, result.desc, result.keywords, int(result.mime), int(result.bad))\n\n\ndef fetch_data(\n        url: str,\n        http_head: bool = False\n) -> FetchResult:\n    \"\"\"Handle server connection and redirections.\n\n    Parameters\n    ----------\n    url : str\n        URL to fetch.\n    http_head : bool\n        If True, send only HTTP HEAD request. Default is False.\n\n    Returns\n    -------\n    FetchResult\n        (url, title, desc, keywords, mime, bad, fetch_status)\n    \"\"\"\n\n    page_status = None\n    page_url = url\n    page_title = ''\n    page_desc = ''\n    page_keys = ''\n    exception = False\n\n    if is_nongeneric_url(url) or is_bad_url(url):\n        return FetchResult(url, bad=True)\n\n    if is_ignored_mime(url) or http_head:\n        method = 'HEAD'\n    else:\n        method = 'GET'\n\n    if not MYHEADERS:\n        gen_headers()\n\n    try:\n        manager = get_PoolManager()\n\n        while True:\n            resp = manager.request(method, url, retries=Retry(redirect=10))\n            page_status = resp.status\n\n            if resp.status == 200:\n                if method == 'GET':\n                    for retry in resp.retries.history:\n                        if retry.status not in PERMANENT_REDIRECTS:\n                            break\n                        page_status, page_url = retry.status, retry.redirect_location\n                    page_title, page_desc, page_keys = get_data_from_page(resp)\n            elif resp.status == 403 and url.endswith('/'):\n                # HTTP response Forbidden\n                # Handle URLs in the form of https://www.domain.com/\n                # which fail when trying to fetch resource '/'\n                # retry without trailing '/'\n\n                LOGDBG('Received status 403: retrying...')\n                # Remove trailing /\n                url = url[:-1]\n                resp.close()\n                continue\n            else:\n                page_title, page_desc, page_keys = get_data_from_page(resp)\n                LOGERR('[%s] %s', resp.status, resp.reason)\n\n            if resp:\n                resp.close()\n\n            break\n    except Exception as e:\n        LOGERR('fetch_data(): %s', e)\n        exception = True\n\n    if manager:\n        manager.clear()\n    if exception:\n        return FetchResult(url)\n    if method == 'HEAD':\n        return FetchResult(url, mime=True, fetch_status=page_status)\n\n    return FetchResult(page_url, title=page_title, desc=page_desc, keywords=page_keys, fetch_status=page_status)\n\n\ndef parse_tags(keywords=[], *, edit_input=False):\n    \"\"\"Format and get tag string from tokens.\n\n    Parameters\n    ----------\n    keywords : list\n        List of tags to parse. Default is empty list.\n    edit_input : bool\n        Whether the taglist is an edit input (i.e. may start with '+'/'-').\n        Defaults to False.\n\n    Returns\n    -------\n    str\n        Comma-delimited string of tags.\n    DELIM : str\n        If no keywords, returns the delimiter.\n    None\n        If keywords is None.\n    \"\"\"\n\n    if keywords is None:\n        return None\n\n    tagstr = ' '.join(s for s in keywords if s)\n    if not tagstr:\n        return DELIM\n\n    if edit_input and keywords[0] in ('+', '-'):\n        return keywords[0] + parse_tags(keywords[1:])\n\n    # Cleanse and get the tags\n    marker = tagstr.find(DELIM)\n    tags = DELIM\n\n    while marker >= 0:\n        token = tagstr[0:marker]\n        tagstr = tagstr[marker + 1:]\n        marker = tagstr.find(DELIM)\n        token = token.strip()\n        if token == '':\n            continue\n\n        tags += token + DELIM\n\n    tagstr = tagstr.strip()\n    if tagstr != '':\n        tags += tagstr + DELIM\n\n    LOGDBG('keywords: %s', keywords)\n    LOGDBG('parsed tags: [%s]', tags)\n\n    if tags == DELIM:\n        return tags\n\n    # sorted unique tags in lowercase, wrapped with delimiter\n    return taglist_str(tags)\n\n\ndef prep_tag_search(tags: str) -> Tuple[List[str], Optional[str], Optional[str]]:\n    \"\"\"Prepare list of tags to search and determine search operator.\n\n    Parameters\n    ----------\n    tags : str\n        String list of tags to search.\n\n    Returns\n    -------\n    tuple\n        (list of formatted tags to search,\n         a string indicating query search operator (either OR or AND),\n         a regex string of tags or None if ' - ' delimiter not in tags).\n    \"\"\"\n\n    exclude_only = False\n\n    # tags may begin with `- ` if only exclusion list is provided\n    if tags.startswith('- '):\n        tags = ' ' + tags\n        exclude_only = True\n\n    # tags may start with `+ ` etc., tricky test case\n    if tags.startswith(('+ ', ', ')):\n        tags = tags[2:]\n\n    # tags may end with ` -` etc., tricky test case\n    if tags.endswith((' -', ' +', ' ,')):\n        tags = tags[:-2]\n\n    # tag exclusion list can be separated by comma (,), so split it first\n    excluded_tags = None\n    if ' - ' in tags:\n        tags, excluded_tags = tags.split(' - ', 1)\n\n        excluded_taglist = [delim_wrap(re.escape(t.strip())) for t in excluded_tags.split(',')]\n        # join with pipe to construct regex string\n        excluded_tags = '|'.join(excluded_taglist)\n\n    if exclude_only:\n        search_operator = 'OR'\n        tags_ = ['']\n    else:\n        # do not allow combination of search logics in tag inclusion list\n        if ' + ' in tags and ',' in tags:\n            return [], None, None\n\n        search_operator = 'OR'\n        tag_delim = ','\n        if ' + ' in tags:\n            search_operator = 'AND'\n            tag_delim = ' + '\n\n        tags_ = [delim_wrap(t.strip()) for t in tags.split(tag_delim)]\n\n    return tags_, search_operator, excluded_tags\n\n\ndef gen_auto_tag():\n    \"\"\"Generate a tag in Year-Month-Date format.\n\n    Returns\n    -------\n    str\n        New tag as YYYYMonDD.\n    \"\"\"\n\n    t = time.localtime()\n    return '%d%s%02d' % (t.tm_year, calendar.month_abbr[t.tm_mon], t.tm_mday)\n\n\ndef edit_at_prompt(obj, nav, suggest=False):\n    \"\"\"Edit and add or update a bookmark.\n\n    Parameters\n    ----------\n    obj : BukuDb instance\n        A valid instance of BukuDb class.\n    nav : str\n        Navigation command argument passed at prompt by user.\n    suggest : bool\n        If True, suggest similar tags on new bookmark addition.\n    \"\"\"\n\n    if nav == 'w':\n        editor = get_system_editor()\n        if not is_editor_valid(editor):\n            return\n    elif is_int(nav[2:]):\n        obj.edit_update_rec(int(nav[2:]))\n        return\n    else:\n        editor = nav[2:]\n\n    result = edit_rec(editor, '', None, DELIM, None)\n    if result is not None:\n        url, title, tags, desc = result\n        if suggest:\n            tags = obj.suggest_similar_tag(tags)\n        obj.add_rec(url, title, tags, desc)\n\n\ndef show_taglist(obj):\n    \"\"\"Additional prompt to show unique tag list.\n\n    Parameters\n    ----------\n    obj : BukuDb instance\n        A valid instance of BukuDb class.\n    \"\"\"\n\n    unique_tags, dic = obj.get_tag_all()\n    if not unique_tags:\n        count = 0\n        print('0 tags')\n    else:\n        count = 1\n        for tag in unique_tags:\n            print('%6d. %s (%d)' % (count, tag, dic[tag]))\n            count += 1\n        print()\n\n\ndef prompt(obj, results, noninteractive=False, deep=False, listtags=False, suggest=False, num=10, markers=False, order=['+id']):\n    \"\"\"Show each matching result from a search and prompt.\n\n    Parameters\n    ----------\n    obj : BukuDb instance\n        A valid instance of BukuDb class.\n    results : list\n        Search result set from a DB query.\n    noninteractive : bool\n        If True, does not seek user input. Shows all results. Default is False.\n    deep : bool\n        Use deep search. Default is False.\n    markers : bool\n        Use search-with-markers. Default is False.\n    listtags : bool\n        If True, list all tags.\n    suggest : bool\n        If True, suggest similar tags on edit and add bookmark.\n    order : list of str\n        Order description (fields from JSON export or DB, prepended with '+'/'-' for ASC/DESC).\n    num : int\n        Number of results to show per page. Default is 10.\n    \"\"\"\n\n    if not isinstance(obj, BukuDb):\n        LOGERR('Not a BukuDb instance')\n        return\n    bdb = obj\n\n    new_results = bool(results)\n    nav = ''\n    cur_index = next_index = prev_index = 0\n\n    if listtags:\n        show_taglist(obj)\n\n    try:\n        columns, _ = os.get_terminal_size()\n    except OSError:\n        columns = 0\n\n    if noninteractive:\n        try:\n            for i, row in enumerate(results or []):\n                print_single_rec(row, i + 1, columns)\n        except Exception as e:\n            LOGERR(e)\n        return\n\n    skip_print = False\n    while True:\n        if (new_results or nav in ['n', 'N']) and not skip_print:\n            _total_results = len(results or [])\n            cur_index = next_index              # used elsewhere as \"most recent page start index\"\n            if not results:\n                print('0 results')\n                new_results = False\n            elif cur_index >= _total_results and nav != 'N':\n                print('No more results')\n                new_results = False\n            else:\n                if nav == 'N':\n                    cur_index = min(cur_index, prev_index)\n                prev_index = max(0, cur_index - num)\n                next_index = min(cur_index + num, _total_results)\n                print()\n                for i in range(cur_index, next_index):\n                    print_single_rec(results[i], i + 1, columns)\n                print('%d-%d/%d' % (cur_index + 1, next_index, _total_results))\n        skip_print = False\n\n        try:\n            prompt_suffix = ''\n            if bdb.dbfile != os.path.realpath(os.path.join(BukuDb.get_default_dbdir(), 'bookmarks.db')):\n                prompt_suffix = (f'[{bdb.dbname}] ' if not bdb.colorize else\n                                 f'\\001\\x1b[7\\002m[{bdb.dbname}]\\001\\x1b[0m\\002 ')\n            nav = read_in(PROMPTMSG + prompt_suffix)\n            if not nav:\n                nav = read_in(PROMPTMSG + prompt_suffix)\n                if not nav:\n                    # Quit on double enter\n                    break\n            nav = nav.strip()\n        except EOFError:\n            return\n\n        # show the next set of results from previous search\n        if nav in ('n', 'N'):\n            continue\n\n        if (m := re.match(r'^R(?: (-)?([0-9]+))?$', nav.rstrip())) and (n := int(m[2] or 1)) > 0:\n            skip_print = True\n            if results and not m[1]:  # from search results\n                picked = random.sample(results, min(n, len(results)))\n            else:                     # from all bookmarks\n                ids = range(1, 1 + (bdb.get_max_id() or 0))\n                picked = bdb.get_rec_all_by_ids(random.sample(ids, min(n, len(ids))))\n            for row in bdb._sort(picked, order):\n                print_single_rec(row, columns=columns)\n            continue\n\n        if (m := re.match(r'^\\^ ([1-9][0-9]*) ([1-9][0-9]*)$', nav.rstrip())):\n            index1, index2 = map(int, m.group(1, 2))\n            if bdb.swap_recs(index1, index2):\n                bdb.print_rec({index1, index2})\n            else:\n                print('Failed to swap records #%d and #%d' % (index1, index2))\n            continue\n\n        # search ANY match with new keywords\n        if nav.startswith('s '):\n            keywords = (nav[2:].split() if not markers else split_by_marker(nav[2:]))\n            results = bdb.searchdb(keywords, deep=deep, markers=markers, order=order)\n            new_results = True\n            cur_index = next_index = 0\n            continue\n\n        # search ALL match with new keywords\n        if nav.startswith('S '):\n            keywords = (nav[2:].split() if not markers else split_by_marker(nav[2:]))\n            results = bdb.searchdb(keywords, all_keywords=True, deep=deep, markers=markers, order=order)\n            new_results = True\n            cur_index = next_index = 0\n            continue\n\n        # regular expressions search with new keywords\n        if nav.startswith('r '):\n            keywords = (nav[2:].split() if not markers else split_by_marker(nav[2:]))\n            results = bdb.searchdb(keywords, all_keywords=True, regex=True, markers=markers, order=order)\n            new_results = True\n            cur_index = next_index = 0\n            continue\n\n        # tag search with new keywords\n        if nav.startswith('t '):\n            results = bdb.search_by_tag(nav[2:], order=order)\n            new_results = True\n            cur_index = next_index = 0\n            continue\n\n        # quit with 'q'\n        if nav == 'q':\n            return\n\n        # No new results fetched beyond this point\n        new_results = False\n\n        # toggle deep search with 'd', search-with-markers with 'm'\n        if nav == 'd':\n            deep = not deep\n            print('deep search', ('on' if deep else 'off'))\n            continue\n        if nav == 'm':\n            markers = not markers\n            print('search-with-markers', ('on' if markers else 'off'))\n            continue\n\n        if re.match(r'v!? ', nav):  # letters 's' and 'o' are taken already\n            _fields = {'metadata': 'title', **JSON_FIELDS}\n            _order = bdb._ordering(filter(None, re.split(r'[,\\s]+', nav[2:].strip())))\n            order_ = [('+' if asc else '-') + _fields.get(s, s) for s, asc in _order]\n            if nav.startswith('v '):\n                order = order_\n                print('order', ', '.join(order))\n            else:\n                bdb.reorder(order_)\n                print('Reindexed bookmarks to match order:', ', '.join(order_))\n            continue\n\n        # Toggle GUI browser with 'O'\n        if nav == 'O':\n            browse.override_text_browser = not browse.override_text_browser\n            print('text browser override toggled')\n            continue\n\n        # Show help with '?'\n        if nav == '?':\n            ExtendedArgumentParser.prompt_help(sys.stdout)\n            continue\n\n        # Edit and add or update\n        if nav == 'w' or nav.startswith('w '):\n            edit_at_prompt(bdb, nav, suggest)\n            continue\n\n        # Append or overwrite tags\n        if nav.startswith('g '):\n            unique_tags, dic = obj.get_tag_all()\n            _count = bdb.set_tag(nav[2:], unique_tags)\n            if _count == -1:\n                print('Invalid input')\n            elif _count == -2:\n                try:\n                    tagid_list = nav[2:].split()\n                    tagstr = bdb.get_tagstr_from_taglist(tagid_list, unique_tags)\n                    tagstr = tagstr.strip(DELIM)\n                    results = bdb.search_by_tag(tagstr)\n                    new_results = True\n                    cur_index = next_index = 0\n                except Exception:\n                    print('Invalid input')\n            else:\n                print('%d updated' % _count)\n            continue\n\n        # Print bookmarks by DB index\n        if nav.startswith('p '):\n            try:\n                ids = parse_range(nav[2:].split(), maxidx=bdb.get_max_id() or 0)\n                ids and bdb.print_rec(ids, order=order)\n            except ValueError:\n                print('Invalid input')\n            continue\n\n        # Browse bookmarks by DB index\n        if nav.startswith('o '):\n            id_list = nav[2:].split()\n            try:\n                for id in id_list:\n                    if is_int(id):\n                        bdb.browse_by_index(int(id))\n                    elif '-' in id:\n                        vals = [int(x) for x in id.split('-')]\n                        bdb.browse_by_index(0, vals[0], vals[-1], True)\n                    else:\n                        print('Invalid input')\n            except ValueError:\n                print('Invalid input')\n            continue\n\n        # Copy URL to clipboard\n        if nav.startswith('c ') and nav[2:].isdigit():\n            index = int(nav[2:]) - 1\n            if index < 0 or index >= next_index:\n                print('No matching index')\n                continue\n            copy_to_clipboard(content=results[index + cur_index][1].encode('utf-8'))\n            continue\n\n        # open all results and re-prompt with 'a'\n        if nav == 'a':\n            for index in range(cur_index, next_index):\n                browse(results[index][1], bdb.default_scheme)\n            continue\n\n        # list tags with 't'\n        if nav == 't':\n            show_taglist(bdb)\n            continue\n\n        if (nav+' ').startswith('DB '):\n            dbpath, dbfile = os.path.split(bdb.dbfile)\n            if nav == 'DB':\n                print(f'Available DB files (in {dbpath}):')\n                for s in sorted(s for s in os.listdir(dbpath) if s.endswith('.db')):\n                    print(('*' if s == dbfile else ' '), s.removesuffix('.db'))\n            else:\n                s = os.path.expanduser(re.sub(r'^DB\\s+', '', nav))\n                path = os.path.join(dbpath, (s if s != '~.' else BukuDb.get_default_dbdir()))  # relative to current dir\n                newpath, newfile = os.path.split(path+'/' if os.path.isdir(path) else path)\n                newfile, (_, ext) = newfile or 'bookmarks', os.path.splitext(newfile)\n                if not ext or os.path.isfile(os.path.join(newpath, newfile+'.db')):\n                    newfile += '.db'\n                newdb = os.path.join(newpath, newfile)\n                try:\n                    if not os.path.exists(newdb):\n                        print(f'DB file is being created at {newdb}')\n                    _bdb = bdb\n                    bdb = BukuDb(json=bdb.json, field_filter=bdb.field_filter, colorize=bdb.colorize, dbfile=newdb,\n                                 default_scheme=bdb.default_scheme)\n                    _bdb.close()\n                    results, new_results = [], False\n                    cur_index = next_index = prev_index = 0\n                    print(f'Loaded DB file at {bdb.dbfile}')\n                except Exception:\n                    print(f'Failed to open DB file at {newdb}')\n            continue\n\n        toggled = False\n        # Open in GUI browser\n        if nav.startswith('O '):\n            if not browse.override_text_browser:\n                browse.override_text_browser = True\n                toggled = True\n            nav = nav[2:]\n\n        # iterate over white-space separated indices\n        for nav in nav.split():\n            if is_int(nav):\n                index = int(nav) - 1\n                if index < 0 or index >= next_index:\n                    print('No matching index %s' % nav)\n                    continue\n                browse(results[index][1], bdb.default_scheme)\n            elif '-' in nav:\n                try:\n                    vals = [int(x) for x in nav.split('-')]\n                    if vals[0] > vals[-1]:\n                        vals[0], vals[-1] = vals[-1], vals[0]\n\n                    for _id in range(vals[0]-1, vals[-1]):\n                        if 0 <= _id < next_index:\n                            browse(results[_id][1], bdb.default_scheme)\n                        else:\n                            print('No matching index %d' % (_id + 1))\n                except ValueError:\n                    print('Invalid input')\n                    break\n            else:\n                print('Invalid input')\n                break\n\n        if toggled:\n            browse.override_text_browser = False\n\n\ndef copy_to_clipboard(content):\n    \"\"\"Copy content to clipboard\n\n    Parameters\n    ----------\n    content : str\n        Content to be copied to clipboard\n    \"\"\"\n\n    # try copying the url to clipboard using native utilities\n    copier_params = []\n    if sys.platform.startswith(('linux', 'freebsd', 'openbsd')):\n        if shutil.which('xsel') is not None:\n            copier_params = ['xsel', '-b', '-i']\n        elif shutil.which('xclip') is not None:\n            copier_params = ['xclip', '-selection', 'clipboard']\n        elif shutil.which('wl-copy') is not None:\n            copier_params = ['wl-copy']\n        # If we're using Termux (Android) use its 'termux-api'\n        # add-on to set device clipboard.\n        elif shutil.which('termux-clipboard-set') is not None:\n            copier_params = ['termux-clipboard-set']\n    elif sys.platform == 'darwin':\n        copier_params = ['pbcopy']\n    elif sys.platform == 'win32':\n        copier_params = ['clip']\n\n    if copier_params:\n        Popen(copier_params, stdin=PIPE, stdout=DEVNULL, stderr=DEVNULL).communicate(content)\n        return\n\n    # If native clipboard utilities are absent, try to use terminal multiplexers\n    # tmux\n    if os.getenv('TMUX_PANE'):\n        copier_params = ['tmux', 'set-buffer']\n        Popen(\n            copier_params + [content],\n            stdin=DEVNULL,\n            stdout=DEVNULL,\n            stderr=DEVNULL\n        ).communicate()\n        print('URL copied to tmux buffer.')\n        return\n\n    # GNU Screen paste buffer\n    if os.getenv('STY'):\n        copier_params = ['screen', '-X', 'readbuf', '-e', 'utf8']\n        tmpfd, tmppath = tempfile.mkstemp()\n        try:\n            with os.fdopen(tmpfd, 'wb') as fp:\n                fp.write(content)\n            copier_params.append(tmppath)\n            Popen(copier_params, stdin=DEVNULL, stdout=DEVNULL, stderr=DEVNULL).communicate()\n        finally:\n            os.unlink(tmppath)\n        return\n\n    print('Failed to locate suitable clipboard utility')\n    return\n\n\ndef print_rec_with_filter(records, field_filter=0):\n    \"\"\"Print records filtered by field.\n\n    User determines which fields in the records to display\n    by using the --format option.\n\n    Parameters\n    ----------\n    records : list or sqlite3.Cursor object\n        List of bookmark records to print\n    field_filter : int\n        Integer indicating which fields to print. Default is 0 (\"all fields\").\n    \"\"\"\n\n    try:\n        records = bookmark_vars(records)\n        fields = FIELD_FILTER.get(field_filter)\n        if fields:\n            pattern = '\\t'.join('%s' for k in fields)\n            for row in records:\n                print(pattern % tuple(getattr(row, k) for k in fields))\n        else:\n            try:\n                columns, _ = os.get_terminal_size()\n            except OSError:\n                columns = 0\n            for row in records:\n                print_single_rec(row, columns=columns)\n    except BrokenPipeError:\n        sys.stdout = os.fdopen(1)\n        sys.exit(1)\n\n\ndef print_single_rec(row: BookmarkVar, idx: int=0, columns: int=0):  # NOQA\n    \"\"\"Print a single DB record.\n\n    Handles both search results and individual record.\n\n    Parameters\n    ----------\n    row : tuple\n        Tuple representing bookmark record data.\n    idx : int\n        Search result index. If 0, print with DB index.\n        Default is 0.\n    columns : int\n        Number of columns to wrap comments to.\n        Default is 0.\n    \"\"\"\n\n    str_list = []\n    row = BookmarkVar(*row)  # ensuring named tuple\n\n    # Start with index and title\n    if idx != 0:\n        id_title_res = ID_STR % (idx, row.title or 'Untitled', row.id)\n    else:\n        id_title_res = ID_DB_STR % (row.id, row.title or 'Untitled')\n        # Indicate if record is immutable\n        if row.immutable:\n            id_title_res = MUTE_STR % (id_title_res,)\n        else:\n            id_title_res += '\\n'\n\n    try:\n        print(id_title_res, end='')\n        print(URL_STR % (row.url,), end='')\n        if columns == 0:\n            if row.desc:\n                print(DESC_STR % (row.desc,), end='')\n            if row.tags:\n                print(TAG_STR % (row.tags,), end='')\n            print()\n            return\n\n        INDENT = 5\n        fillwidth = columns - INDENT\n        desc_lines = [s for line in row.desc.splitlines()\n                        for s in textwrap.wrap(line, width=fillwidth) or ['']]\n        TR = str.maketrans(',-', '-,')  # we want breaks after commas rather than hyphens\n        tag_lines = textwrap.wrap(row.tags.translate(TR), width=fillwidth)\n\n        for idx, line in enumerate(desc_lines):\n            if idx == 0:\n                print(DESC_STR % line, end='')\n            else:\n                print(DESC_WRAP % (' ' * INDENT, line))\n\n        for idx, line in enumerate(tag_lines):\n            if idx == 0:\n                print(TAG_STR % line.translate(TR), end='')\n            else:\n                print(TAG_WRAP % (' ' * INDENT, line.translate(TR)))\n        print()\n    except UnicodeEncodeError:\n        str_list = []\n        str_list.append(id_title_res)\n        str_list.append(URL_STR % (row.url,))\n        if row.desc:\n            str_list.append(DESC_STR % (row.desc,))\n        if row.tags:\n            str_list.append(TAG_STR % (row.tags,))\n        sys.stdout.buffer.write((''.join(str_list) + '\\n').encode('utf-8'))\n    except BrokenPipeError:\n        sys.stdout = os.fdopen(1)\n        sys.exit(1)\n\n\ndef write_string_to_file(content: str, filepath: str):\n    \"\"\"Writes given content to file\n\n    Parameters\n    ----------\n    content : str\n    filepath : str\n\n    Returns\n    -------\n    None\n    \"\"\"\n    try:\n        with open(filepath, 'w', encoding='utf-8') as f:\n            f.write(content)\n    except Exception as e:\n        LOGERR(e)\n\n\ndef format_json(resultset, single_record=False, field_filter=0):\n    \"\"\"Return results in JSON format.\n\n    Parameters\n    ----------\n    resultset : list\n        Search results from DB query.\n    single_record : bool\n        If True, indicates only one record. Default is False.\n    field_filter : int\n        Indicates format for displaying bookmarks. Default is 0 (\"all fields\").\n\n    Returns\n    -------\n    json\n        Record(s) in JSON format.\n    \"\"\"\n\n    resultset = bookmark_vars(resultset)\n    fields = [(k, JSON_FIELDS.get(k, k)) for k in FIELD_FILTER.get(field_filter, ALL_FIELDS)]\n    marks = [{field: getattr(row, k) for k, field in fields} for row in resultset]\n    if single_record:\n        marks = marks[-1] if marks else {}\n\n    return json.dumps(marks, sort_keys=True, indent=4)\n\n\ndef print_json_safe(resultset, single_record=False, field_filter=0):\n    \"\"\"Prints json results and handles IOError\n\n    Parameters\n    ----------\n    resultset : list\n        Search results from DB query.\n    single_record : bool\n        If True, indicates only one record. Default is False.\n    field_filter : int\n        Indicates format for displaying bookmarks. Default is 0 (\"all fields\").\n\n    Returns\n    -------\n    None\n    \"\"\"\n\n    try:\n        print(format_json(resultset, single_record, field_filter))\n    except IOError:\n        try:\n            sys.stdout.close()\n        except IOError:\n            pass\n        try:\n            sys.stderr.close()\n        except IOError:\n            pass\n\n\ndef is_int(string):\n    \"\"\"Check if a string is a digit.\n\n    string : str\n        Input string to check.\n\n    Returns\n    -------\n    bool\n        True on success, False on exception.\n    \"\"\"\n\n    try:\n        int(string)\n        return True\n    except Exception:\n        return False\n\n\ndef browse(url, default_scheme=SCHEME_HTTP):\n    \"\"\"Duplicate stdin, stdout and open URL in default browser.\n\n    .. Note:: Duplicates stdin and stdout in order to\n              suppress showing errors on the terminal.\n\n    Parameters\n    ----------\n    url : str\n        URL to open in browser.\n    default_scheme : str\n        Scheme to assume if missing from URL. Default is http.\n\n    Attributes\n    ----------\n    suppress_browser_output : bool\n        True if a text based browser is detected.\n        Must be initialized (as applicable) to use the API.\n    override_text_browser : bool\n        If True, tries to open links in a GUI based browser.\n    \"\"\"\n\n    if not urlparse(url).scheme:\n        # Prefix with default_scheme to improve the chance\n        # it will open properly in the browser\n        LOGERR('Scheme missing in URI, trying %s', default_scheme)\n        url = f'{default_scheme}://{url}'\n\n    browser = webbrowser.get()\n    if browse.override_text_browser:\n        browser_output = browse.suppress_browser_output\n        for name in [b for b in webbrowser._tryorder if b not in TEXT_BROWSERS]:\n            browser = webbrowser.get(name)\n            LOGDBG(browser)\n\n            # Found a GUI browser, suppress browser output\n            browse.suppress_browser_output = True\n            break\n    if sys.platform == 'win32':  # GUI apps have no terminal IO on Windows\n        browse.suppress_browser_output = False\n\n    if browse.suppress_browser_output:\n        _stderr = os.dup(2)\n        os.close(2)\n        _stdout = os.dup(1)\n        if \"microsoft\" not in platform.uname()[3].lower():\n            os.close(1)\n        fd = os.open(os.devnull, os.O_RDWR)\n        os.dup2(fd, 2)\n        os.dup2(fd, 1)\n    try:\n        if sys.platform != 'win32':\n            browser.open(url, new=2)\n        else:\n            # On Windows, the webbrowser module does not fork.\n            # Use threads instead.\n            def browserthread():\n                webbrowser.open(url, new=2)\n\n            t = threading.Thread(target=browserthread)\n            t.start()\n    except Exception as e:\n        LOGERR('browse(): %s', e)\n    finally:\n        if browse.suppress_browser_output:\n            os.close(fd)\n            os.dup2(_stderr, 2)\n            os.dup2(_stdout, 1)\n\n    if browse.override_text_browser:\n        browse.suppress_browser_output = browser_output\n\n\ndef check_upstream_release():\n    \"\"\"Check and report the latest upstream release version.\"\"\"\n\n    if MYPROXY is None:\n        gen_headers()\n\n    ca_certs = os.getenv('BUKU_CA_CERTS', default=CA_CERTS)\n    if MYPROXY:\n        manager = urllib3.ProxyManager(\n            MYPROXY,\n            num_pools=1,\n            headers=MYHEADERS,\n            cert_reqs='CERT_REQUIRED',\n            ca_certs=ca_certs\n        )\n    else:\n        manager = urllib3.PoolManager(num_pools=1,\n                                      headers={'User-Agent': USER_AGENT},\n                                      cert_reqs='CERT_REQUIRED',\n                                      ca_certs=ca_certs)\n\n    try:\n        r = manager.request(\n            'GET',\n            'https://api.github.com/repos/jarun/buku/releases?per_page=1',\n            headers={'User-Agent': USER_AGENT}\n        )\n    except Exception as e:\n        LOGERR(e)\n        return\n\n    if r.status == 200:\n        latest = json.loads(r.data.decode(errors='replace'))[0]['tag_name']\n        if latest == 'v' + __version__:\n            print('This is the latest release')\n        else:\n            print('Latest upstream release is %s' % latest)\n    else:\n        LOGERR('[%s] %s', r.status, r.reason)\n\n    manager.clear()\n\n\ndef regexp(expr, item):\n    \"\"\"Perform a regular expression search.\n\n    Parameters\n    ----------\n    expr : regex\n        Regular expression to search for.\n    item : str\n        Item on which to perform regex search.\n\n    Returns\n    -------\n    bool\n        True if result of search is not None, else False.\n    \"\"\"\n\n    if expr is None or item is None:\n        LOGDBG('expr: [%s], item: [%s]', expr, item)\n        return False\n\n    return re.search(expr, item, re.IGNORECASE) is not None\n\n\ndef delim_wrap(token):\n    \"\"\"Returns token string wrapped in delimiters.\n\n    Parameters\n    ----------\n    token : str\n        String item to wrap with DELIM.\n\n    Returns\n    -------\n    str\n        Token string wrapped by DELIM.\n    \"\"\"\n\n    if token is None or token.strip() == '':\n        return DELIM\n\n    if token[0] != DELIM:\n        token = DELIM + token\n\n    if token[-1] != DELIM:\n        token = token + DELIM\n\n    return token\n\n\ndef read_in(msg):\n    \"\"\"A wrapper to handle input() with interrupts disabled.\n\n    Parameters\n    ----------\n    msg : str\n        String to pass to to input().\n    \"\"\"\n\n    disable_sigint_handler()\n    message = None\n    try:\n        message = input(msg)\n    except KeyboardInterrupt:\n        print('Interrupted.')\n\n    enable_sigint_handler()\n    return message\n\n\ndef sigint_handler(signum, frame):\n    \"\"\"Custom SIGINT handler.\n\n    .. Note:: Neither signum nor frame are used in\n              this custom handler. However, they are\n              required parameters for signal handlers.\n\n    Parameters\n    ----------\n    signum : int\n        Signal number.\n    frame : frame object or None.\n    \"\"\"\n\n    global INTERRUPTED\n\n    INTERRUPTED = True\n    print('\\nInterrupted.', file=sys.stderr)\n\n    # Do a hard exit from here\n    os._exit(1)\n\nDEFAULT_HANDLER = signal.signal(signal.SIGINT, sigint_handler)\n\n\ndef disable_sigint_handler():\n    \"\"\"Disable signint handler.\"\"\"\n    signal.signal(signal.SIGINT, DEFAULT_HANDLER)\n\n\ndef enable_sigint_handler():\n    \"\"\"Enable sigint handler.\"\"\"\n    signal.signal(signal.SIGINT, sigint_handler)\n\n# ---------------------\n# Editor mode functions\n# ---------------------\n\n\ndef get_system_editor():\n    \"\"\"Returns default system editor is $EDITOR is set.\"\"\"\n\n    return os.environ.get('EDITOR', 'none')\n\n\ndef is_editor_valid(editor):\n    \"\"\"Check if the editor string is valid.\n\n    Parameters\n    ----------\n    editor : str\n        Editor string.\n\n    Returns\n    -------\n    bool\n        True if string is valid, else False.\n    \"\"\"\n\n    if editor == 'none':\n        LOGERR('EDITOR is not set')\n        return False\n\n    if editor == '0':\n        LOGERR('Cannot edit index 0')\n        return False\n\n    return True\n\n\ndef to_temp_file_content(url, title_in, tags_in, desc):\n    \"\"\"Generate temporary file content string.\n\n    Parameters\n    ----------\n    url : str\n        URL to open.\n    title_in : str\n        Title to add manually.\n    tags_in : str\n        Comma-separated tags to add manually.\n    desc : str\n        String description.\n\n    Returns\n    -------\n    str\n        Lines as newline separated string.\n\n    Raises\n    ------\n    AttributeError\n        when tags_in is None.\n    \"\"\"\n\n    strings = [('# Lines beginning with \"#\" will be stripped.\\n'\n                '# Add URL in next line (single line).'), ]\n\n    # URL\n    if url is not None:\n        strings += (url,)\n\n    # TITLE\n    strings += (('# Add TITLE in next line (single line). '\n                 'Leave blank to web fetch, \"-\" for no title.'),)\n    if title_in is None:\n        title_in = ''\n    elif title_in == '':\n        title_in = '-'\n    strings += (title_in,)\n\n    # TAGS\n    strings += ('# Add comma-separated TAGS in next line (single line).',)\n    strings += [(tags_in or '').strip(DELIM)]\n\n    # DESC\n    strings += ('# Add COMMENTS in next line(s). Leave blank to web fetch, \"-\" for no comments.',)\n    if desc is None:\n        strings += ('\\n',)\n    elif desc == '':\n        strings += ('-',)\n    else:\n        strings += (desc,)\n    return '\\n'.join(strings)\n\n\ndef parse_temp_file_content(content):\n    \"\"\"Parse and return temporary file content.\n\n    Parameters\n    ----------\n    content : str\n        String of content.\n\n    Returns\n    -------\n    tuple\n        (url, title, tags, comments)\n\n        url: URL to open\n        title: string title to add manually\n        tags: string of comma-separated tags to add manually\n        comments: string description\n    \"\"\"\n\n    content = content.split('\\n')\n    content = [c for c in content if not c or c[0] != '#']\n    if not content or content[0].strip() == '':\n        print('Edit aborted')\n        return None\n\n    url = content[0]\n    title = None\n    if len(content) > 1:\n        title = content[1]\n\n    if title == '':\n        title = None\n    elif title == '-':\n        title = ''\n\n    tags = DELIM\n    if len(content) > 2:\n        tags = parse_tags([content[2]])\n\n    comments = []\n    if len(content) > 3:\n        comments = list(content[3:])\n        # need to remove all empty line that are at the end\n        # and not those in the middle of the text\n        for i in range(len(comments) - 1, -1, -1):\n            if comments[i].strip() != '':\n                break\n\n        if i == -1:\n            comments = []\n        else:\n            comments = comments[0:i+1]\n\n    comments = '\\n'.join(comments)\n    if comments == '':\n        comments = None\n    elif comments == '-':\n        comments = ''\n\n    return url, title, tags, comments\n\n\ndef edit_rec(editor, url, title_in, tags_in, desc):\n    \"\"\"Edit a bookmark record.\n\n    Parameters\n    ----------\n    editor : str\n        Editor to open.\n    URL : str\n        URL to open.\n    title_in : str\n        Title to add manually.\n    tags_in : str\n        Comma-separated tags to add manually.\n    desc : str\n        Bookmark description.\n\n    Returns\n    -------\n    tuple\n        Parsed results from parse_temp_file_content().\n    \"\"\"\n\n    temp_file_content = to_temp_file_content(url, title_in, tags_in, desc)\n\n    fd, tmpfile = tempfile.mkstemp(prefix='buku-edit-')\n    os.close(fd)\n\n    try:\n        with open(tmpfile, 'w+', encoding='utf-8') as fp:\n            fp.write(temp_file_content)\n            fp.flush()\n            LOGDBG('Edited content written to %s', tmpfile)\n\n        cmd = editor.split(' ')\n        cmd += (tmpfile,)\n        subprocess.call(cmd)\n\n        with open(tmpfile, 'r', encoding='utf-8') as f:\n            content = f.read()\n\n        os.remove(tmpfile)\n    except FileNotFoundError:\n        if os.path.exists(tmpfile):\n            os.remove(tmpfile)\n            LOGERR('Cannot open editor')\n        else:\n            LOGERR('Cannot open tempfile')\n        return None\n\n    parsed_content = parse_temp_file_content(content)\n    return parsed_content\n\n\ndef setup_logger(LOGGER):\n    \"\"\"Setup logger with color.\n\n    Parameters\n    ----------\n    LOGGER : logger object\n        Logger to colorize.\n    \"\"\"\n\n    def decorate_emit(fn):\n        def new(*args):\n            levelno = args[0].levelno\n\n            if levelno == logging.DEBUG:\n                color = '\\x1b[35m'\n            elif levelno == logging.ERROR:\n                color = '\\x1b[31m'\n            elif levelno == logging.WARNING:\n                color = '\\x1b[33m'\n            elif levelno == logging.INFO:\n                color = '\\x1b[32m'\n            elif levelno == logging.CRITICAL:\n                color = '\\x1b[31m'\n            else:\n                color = '\\x1b[0m'\n\n            args[0].msg = '{}[{}]\\x1b[0m {}'.format(color, args[0].levelname, args[0].msg)\n            return fn(*args)\n        return new\n\n    sh = logging.StreamHandler()\n    sh.emit = decorate_emit(sh.emit)\n    LOGGER.addHandler(sh)\n\n\ndef piped_input(argv, pipeargs=None):\n    \"\"\"Handle piped input.\n\n    Parameters\n    ----------\n    pipeargs : str\n    \"\"\"\n    if not sys.stdin.isatty():\n        pipeargs += argv\n        print('buku: waiting for input (unexpected? try --nostdin)')\n        for s in sys.stdin:\n            pipeargs += s.split()\n\n\ndef setcolors(args):\n    \"\"\"Get colors from user and separate into 'result' list for use in arg.colors.\n\n    Parameters\n    ----------\n    args : str\n        Color string.\n    \"\"\"\n    Colors = collections.namedtuple('Colors', ' ID_srch, ID_STR, URL_STR, DESC_STR, TAG_STR')\n    colors = Colors(*[COLORMAP[c] for c in args])\n    id_col = colors.ID_srch\n    id_str_col = colors.ID_STR\n    url_col = colors.URL_STR\n    desc_col = colors.DESC_STR\n    tag_col = colors.TAG_STR\n    result = [id_col, id_str_col, url_col, desc_col, tag_col]\n    return result\n\n\ndef unwrap(text):\n    \"\"\"Unwrap text.\"\"\"\n    lines = text.split('\\n')\n    result = ''\n    for i in range(len(lines) - 1):\n        result += lines[i]\n        if not lines[i]:\n            # Paragraph break\n            result += '\\n\\n'\n        elif lines[i + 1]:\n            # Next line is not paragraph break, add space\n            result += ' '\n    # Handle last line\n    result += lines[-1] if lines[-1] else '\\n'\n    return result\n\n\ndef check_stdout_encoding():\n    \"\"\"Make sure stdout encoding is utf-8.\n\n    If not, print error message and instructions, then exit with\n    status 1.\n\n    This function is a no-op on win32 because encoding on win32 is\n    messy, and let's just hope for the best. /s\n    \"\"\"\n    if sys.platform == 'win32':\n        return\n\n    # Use codecs.lookup to resolve text encoding alias\n    encoding = codecs.lookup(sys.stdout.encoding).name\n    if encoding != 'utf-8':\n        locale_lang, locale_encoding = locale.getlocale()\n        if locale_lang is None:\n            locale_lang = '<unknown>'\n        if locale_encoding is None:\n            locale_encoding = '<unknown>'\n        ioencoding = os.getenv('PYTHONIOENCODING', 'not set')\n        sys.stderr.write(unwrap(textwrap.dedent(\"\"\"\\\n        stdout encoding '{encoding}' detected. ddgr requires utf-8 to\n        work properly. The wrong encoding may be due to a non-UTF-8\n        locale or an improper PYTHONIOENCODING. (For the record, your\n        locale language is {locale_lang} and locale encoding is\n        {locale_encoding}; your PYTHONIOENCODING is {ioencoding}.)\n\n        Please set a UTF-8 locale (e.g., en_US.UTF-8) or set\n        PYTHONIOENCODING to utf-8.\n        \"\"\".format(\n            encoding=encoding,\n            locale_lang=locale_lang,\n            locale_encoding=locale_encoding,\n            ioencoding=ioencoding,\n        ))))\n        sys.exit(1)\n\n\ndef monkeypatch_textwrap_for_cjk():\n    \"\"\"Monkeypatch textwrap for CJK wide characters.\n    \"\"\"\n    try:\n        if textwrap.wrap.patched:\n            return\n    except AttributeError:\n        pass\n    psl_textwrap_wrap = textwrap.wrap\n\n    def textwrap_wrap(text, width=70, **kwargs):\n        width = max(width, 2)\n        # We first add a U+0000 after each East Asian Fullwidth or East\n        # Asian Wide character, then fill to width - 1 (so that if a NUL\n        # character ends up on a new line, we still have one last column\n        # to spare for the preceding wide character). Finally we strip\n        # all the NUL characters.\n        #\n        # East Asian Width: https://www.unicode.org/reports/tr11/\n        return [\n            line.replace('\\0', '')\n            for line in psl_textwrap_wrap(\n                ''.join(\n                    ch + '\\0' if unicodedata.east_asian_width(ch) in ('F', 'W') else ch\n                    for ch in unicodedata.normalize('NFC', text)\n                ),\n                width=width - 1,\n                **kwargs\n            )\n        ]\n\n    def textwrap_fill(text, width=70, **kwargs):\n        return '\\n'.join(textwrap_wrap(text, width=width, **kwargs))\n    textwrap.wrap = textwrap_wrap\n    textwrap.fill = textwrap_fill\n    textwrap.wrap.patched = True\n    textwrap.fill.patched = True\n\n\ndef parse_range(tokens: Optional[str | Values[str]],\n                valid: Optional[Callable[[int], bool]] = None,\n                maxidx: int = None) -> Optional[Set[int]]:\n    \"\"\"Convert a token or sequence/set of token into a set of indices.\n\n    Raises a ValueError on invalid token. Returns None if passed None as tokens.\n\n    Parameters\n    ----------\n    tokens : str | str[] | str{}, optional\n        String(s) containing an index (#), or a range (#-#), or a comma-separated list thereof.\n    valid : (int) -> bool, optional\n        Additional check for invalid indices (default is None).\n    maxidx : int, optional\n        When specified, negative indices are valid and parsed as tail-ranges.\n\n    Returns\n    -------\n    Optional[Set[int]]\n        None if tokens is None, otherwise parsed indices as unordered set.\n    \"\"\"\n    if tokens is None:\n        return None\n    result = set()\n    for token in ([tokens] if isinstance(tokens, str) else tokens):\n        for idx in token.split(','):\n            if is_int(idx):\n                result |= ({int(idx)} if not idx.startswith('-') or maxidx is None else\n                           set(range(maxidx, max(0, maxidx + int(idx)), -1)))\n            elif '-' in idx:\n                l, r = map(int, idx.split('-'))\n                if l > r:\n                    l, r = r, l\n                if maxidx is not None:\n                    r = min(r, maxidx)\n                result |= set(range(l, r + 1))\n            elif idx:\n                raise ValueError(f'Invalid token: {idx}')\n    if valid and any(not valid(idx) for idx in result):\n        raise ValueError('Not a valid range')\n    return result\n\n\n# main starts here\ndef main(argv=sys.argv[1:], *, program_name=os.path.basename(sys.argv[0])):\n    \"\"\"Main.\"\"\"\n    global ID_STR, ID_DB_STR, MUTE_STR, URL_STR, DESC_STR, DESC_WRAP, TAG_STR, TAG_WRAP, PROMPTMSG\n    # readline should not be loaded when buku is used as a library\n    import readline\n    if sys.platform == 'win32':\n        try:\n            import colorama\n            colorama.just_fix_windows_console()  # noop on non-Windows systems\n        except ImportError:\n            pass\n\n    title_in = None\n    tags_in = None\n    desc_in = None\n    pipeargs = []\n    colorstr_env = os.getenv('BUKU_COLORS')\n\n    if argv == ['--db']:\n        for s in sorted(s for s in os.listdir(BukuDb.get_default_dbdir()) if s.endswith('.db')):\n            print(s.removesuffix('.db'))\n        return\n\n    if argv and argv[0] != '--nostdin':\n        try:\n            piped_input(argv, pipeargs)\n        except KeyboardInterrupt:\n            pass\n\n        # If piped input, set argument vector\n        if pipeargs:\n            argv = pipeargs\n\n    # Setup custom argument parser\n    argparser = ExtendedArgumentParser(\n        prog=program_name,\n        description='''Bookmark manager like a text-based mini-web.\n\nPOSITIONAL ARGUMENTS:\n      KEYWORD              search keywords''',\n        formatter_class=argparse.RawTextHelpFormatter,\n        usage='''buku [OPTIONS] [KEYWORD [KEYWORD ...]]''',\n        add_help=False)\n    hide = argparse.SUPPRESS\n\n    argparser.add_argument('keywords', nargs='*', metavar='KEYWORD', help=hide)\n\n    # ---------------------\n    # GENERAL OPTIONS GROUP\n    # ---------------------\n\n    general_grp = argparser.add_argument_group(\n        title='GENERAL OPTIONS',\n        description='''    -a, --add URL [+|-] [tag, ...]\n                         bookmark URL with comma-separated tags\n                         (prepend tags with '+' or '-' to use fetched tags)\n    -u, --update [...]   update fields of an existing bookmark\n                         accepts indices and ranges\n                         refresh title and desc if no edit options\n                         if no arguments:\n                         - update results when used with search\n                         - otherwise refresh all titles and desc\n    -w, --write [editor|index]\n                         edit and add a new bookmark in editor\n                         else, edit bookmark at index in EDITOR\n                         edit last bookmark, if index=-1\n                         if no args, edit new bookmark in EDITOR\n    -d, --delete [...]   remove bookmarks from DB\n                         accepts indices or a single range\n                         if no arguments:\n                         - delete results when used with search\n                         - otherwise delete all bookmarks\n    --retain-order       prevents reordering after deleting a bookmark\n    -h, --help           show this information and exit\n    -v, --version        show the program version and exit''')\n    addarg = general_grp.add_argument\n    addarg('-a', '--add', nargs='+', help=hide)\n    addarg('-u', '--update', nargs='*', help=hide)\n    addarg('-w', '--write', nargs='?', const=get_system_editor(), help=hide)\n    addarg('-d', '--delete', nargs='*', help=hide)\n    addarg('--retain-order', action='store_true', default=False, help=hide)\n    addarg('-h', '--help', action='store_true', help=hide)\n    addarg('-v', '--version', action='version', version=__version__, help=hide)\n\n    # ------------------\n    # EDIT OPTIONS GROUP\n    # ------------------\n\n    edit_grp = argparser.add_argument_group(\n        title='EDIT OPTIONS',\n        description='''    --url keyword        bookmark link\n    --tag [+|-] [...]    comma-separated tags\n                         clear bookmark tagset, if no arguments\n                         '+' appends to, '-' removes from tagset\n    --title [...]        bookmark title; if no arguments:\n                         -a: do not set title, -u: clear title\n    -c, --comment [...]  notes or description of the bookmark\n                         clears description, if no arguments\n    --immutable N        disable web-fetch during auto-refresh\n                         N=0: mutable (default), N=1: immutable\n    --swap N M           swap two records at specified indices''')\n    addarg = edit_grp.add_argument\n    addarg('--url', nargs=1, help=hide)\n    addarg('--tag', nargs='*', help=hide)\n    addarg('--title', nargs='*', help=hide)\n    addarg('-c', '--comment', nargs='*', help=hide)\n    addarg('--immutable', type=int, choices={0, 1}, help=hide)\n    addarg('--swap', nargs=2, type=int, help=hide)\n    _bool = lambda x: x if x is None else bool(x)\n    _immutable = lambda args: _bool(args.immutable)\n\n    # --------------------\n    # SEARCH OPTIONS GROUP\n    # --------------------\n\n    search_grp = argparser.add_argument_group(\n        title='SEARCH OPTIONS',\n        description='''    -s, --sany [...]     find records with ANY matching keyword\n                         this is the default search option\n    -S, --sall [...]     find records matching ALL the keywords\n                         special keywords -\n                         \"blank\": entries with empty title/tag\n                         \"immutable\": entries with locked title\n    --deep               match substrings ('pen' matches 'opens')\n    --markers            search for keywords in specific fields\n                         based on (optional) prefix markers:\n                         '.' - title, '>' - description, ':' - URL,\n                         '#' - tags (comma-separated, PARTIAL matches)\n                         '#,' - tags (comma-separated, EXACT matches)\n                         '*' - any field (same as no prefix)\n    -r, --sreg expr      run a regex search\n    -t, --stag [tag [,|+] ...] [- tag, ...]\n                         search bookmarks by tags\n                         use ',' to find entries matching ANY tag\n                         use '+' to find entries matching ALL tags\n                         excludes entries with tags after ' - '\n                         list all tags, if no search keywords\n    -x, --exclude [...]  omit records matching specified keywords\n    --random [N]         output random bookmarks out of the selection (default 1)\n    --order fields [...] comma-separated list of fields to order the output by\n                         (prepend with '+'/'-' to choose sort direction)''')\n    addarg = search_grp.add_argument\n    addarg('-s', '--sany', nargs='*', help=hide)\n    addarg('-S', '--sall', nargs='*', help=hide)\n    addarg('-r', '--sreg', nargs='*', help=hide)\n    addarg('--deep', action='store_true', help=hide)\n    addarg('--markers', action='store_true', help=hide)\n    addarg('-t', '--stag', nargs='*', help=hide)\n    addarg('-x', '--exclude', nargs='*', help=hide)\n    addarg('--random', nargs='?', type=int, const=1, help=hide)\n    addarg('--order', nargs='+', help=hide)\n\n    # ------------------------\n    # ENCRYPTION OPTIONS GROUP\n    # ------------------------\n\n    crypto_grp = argparser.add_argument_group(\n        title='ENCRYPTION OPTIONS',\n        description='''    -l, --lock [N]       encrypt DB in N (default 8) # iterations\n    -k, --unlock [N]     decrypt DB in N (default 8) # iterations''')\n    addarg = crypto_grp.add_argument\n    addarg('-k', '--unlock', nargs='?', type=int, const=8, help=hide)\n    addarg('-l', '--lock', nargs='?', type=int, const=8, help=hide)\n\n    # ----------------\n    # POWER TOYS GROUP\n    # ----------------\n\n    power_grp = argparser.add_argument_group(\n        title='POWER TOYS',\n        description='''    --ai                 auto-import bookmarks from web browsers\n                         Firefox, Chrome, Chromium, Vivaldi, Brave, Edge\n                         (Firefox profile can be specified using\n                         environment variable FIREFOX_PROFILE)\n    -e, --export file    export bookmarks to Firefox format HTML\n                         export XBEL, if file ends with '.xbel'\n                         export Markdown, if file ends with '.md'\n                         format: [title](url) <!-- TAGS -->\n                         export Orgfile, if file ends with '.org'\n                         format: *[[url][title]] :tags:\n                         export rss feed if file ends with '.rss'/'.atom'\n                         export buku DB, if file ends with '.db'\n                         combines with search results, if opted\n    -i, --import file    import bookmarks from file\n                         supports .html .xbel .json .md .org .rss .atom .db\n                         (.json = Firefox backup; .db = another Buku DB)\n    -p, --print [...]    show record details by indices, ranges\n                         print all bookmarks, if no arguments\n                         -n shows the last n results (like tail)\n    -f, --format N       limit fields in -p or JSON search output\n                         N=1: URL; N=2: URL, tag; N=3: title;\n                         N=4: URL, title, tag; N=5: title, tag;\n                         N0 (10, 20, 30, 40, 50) omits DB index\n    -j, --json [file]    JSON formatted output for -p and search.\n                         prints to stdout if argument missing.\n                         otherwise writes to given file\n    --colors COLORS      set output colors in five-letter string\n    --nc                 disable color output\n    -n, --count N        show N results per page (default 10)\n    --np                 do not show the subprompt, run and exit\n    -o, --open [...]     browse bookmarks by indices and ranges\n                         open a random bookmark, if no arguments\n    --oa                 browse all search results immediately\n    --default-scheme S   if scheme is missing from uri, assume S\n                         when opening in browser (default http)\n    --replace old new    replace old tag with new tag everywhere\n                         delete old tag, if new tag not specified\n    --url-redirect       when fetching an URL, use the resulting\n                         URL from following *permanent* redirects\n                         (when combined with --export, the old URL\n                         is included as additional metadata)\n    --tag-redirect [tag] when fetching an URL that causes permanent\n                         redirect, add a tag in specified pattern\n                         (using 'http:{}' if not specified)\n    --tag-error [tag]    when fetching an URL that causes an HTTP\n                         error, add a tag in specified pattern\n                         (using 'http:{}' if not specified)\n    --del-error [...]    when fetching an URL causes any (given)\n                         HTTP error, delete/do not add it\n    --export-on [...]    export records affected by the above\n                         options, including removed info\n                         (requires --update and --export; specific\n                         HTTP response filter can be provided)\n    --reorder order...   update DB indices to match specified order\n    --cached index|URL   browse a cached page from Wayback Machine\n    --offline            add a bookmark without connecting to web\n    --suggest            show similar tags when adding bookmarks\n    --tacit              reduce verbosity, skip some confirmations\n    --nostdin            do not wait for input (must be first arg)\n    --threads N          max network connections in full refresh\n                         default N=4, min N=1, max N=10\n    -V                   check latest upstream version available\n    -g, --debug          show debug information and verbose logs''')\n    addarg = power_grp.add_argument\n    addarg('--ai', action='store_true', help=hide)\n    addarg('-e', '--export', nargs=1, help=hide)\n    addarg('-i', '--import', nargs=1, dest='importfile', help=hide)\n    addarg('-p', '--print', nargs='*', help=hide)\n    addarg('-f', '--format', type=int, default=0, choices={1, 2, 3, 4, 5, 10, 20, 30, 40, 50}, help=hide)\n    addarg('-j', '--json', nargs='?', default=None, const='', help=hide)\n    addarg('--colors', dest='colorstr', type=argparser.is_colorstr, metavar='COLORS', help=hide)\n    addarg('--nc', action='store_true', help=hide)\n    addarg('-n', '--count', nargs='?', const=10, type=int, default=0, help=hide)\n    addarg('--np', action='store_true', help=hide)\n    addarg('-o', '--open', nargs='*', help=hide)\n    addarg('--oa', action='store_true', help=hide)\n    addarg('--default-scheme', nargs=1, default=[SCHEME_HTTP], help=hide)\n    addarg('--replace', nargs='+', help=hide)\n    addarg('--url-redirect', action='store_true', help=hide)\n    addarg('--tag-redirect', nargs='?', const=True, default=False, help=hide)\n    addarg('--tag-error', nargs='?', const=True, default=False, help=hide)\n    addarg('--del-error', nargs='*', help=hide)\n    addarg('--export-on', nargs='*', help=hide)\n    addarg('--reorder', nargs='+', help=hide)\n    addarg('--cached', nargs=1, help=hide)\n    addarg('--offline', action='store_true', help=hide)\n    addarg('--suggest', action='store_true', help=hide)\n    addarg('--tacit', action='store_true', help=hide)\n    addarg('--nostdin', action='store_true', help=hide)\n    addarg('--threads', type=int, default=4, choices=range(1, 11), help=hide)\n    addarg('-V', dest='upstream', action='store_true', help=hide)\n    addarg('-g', '--debug', action='store_true', help=hide)\n    # Undocumented APIs\n    # Fix uppercase tags allowed in releases before v2.7\n    addarg('--fixtags', action='store_true', help=hide)\n    # App-use only, not for manual usage\n    addarg('--db', nargs=1, default=[None], help=hide)\n\n    # Parse the arguments\n    args = argparser.parse_args(argv)\n\n    # Show help and exit if help requested\n    if args.help:\n        argparser.print_help()\n        sys.exit(0)\n\n    # By default, buku uses ANSI colors. As Windows does not really use them,\n    # we'd better check for known working console emulators first. Currently,\n    # only ConEmu is supported. If the user does not use ConEmu, colors are\n    # disabled unless --colors or %BUKU_COLORS% is specified.\n    if sys.platform == 'win32' and os.environ.get('ConemuDir') is None:\n        if args.colorstr is None and colorstr_env is not None:\n            args.nc = True\n\n    # Handle NO_COLOR as well:\n    if os.environ.get('NO_COLOR') is not None:\n        args.nc = True\n\n    # Handle color output preference\n    if args.nc:\n        logging.basicConfig(format='[%(levelname)s] %(message)s')\n    else:\n        # Set colors\n        if colorstr_env is not None:\n            # Someone set BUKU_COLORS.\n            colorstr = colorstr_env\n        elif args.colorstr is not None:\n            colorstr = args.colorstr\n        else:\n            colorstr = 'oKlxm'\n\n        ID = setcolors(colorstr)[0] + '%d. ' + COLORMAP['x']\n        ID_DB_dim = COLORMAP['z'] + '[%s]\\n' + COLORMAP['x']\n        ID_STR = ID + setcolors(colorstr)[1] + '%s ' + COLORMAP['x'] + ID_DB_dim\n        ID_DB_STR = ID + setcolors(colorstr)[1] + '%s' + COLORMAP['x']\n        MUTE_STR = '%s \\x1b[2m(L)\\x1b[0m\\n'\n        URL_STR = COLORMAP['j'] + '   > ' + setcolors(colorstr)[2] + '%s\\n' + COLORMAP['x']\n        DESC_STR = COLORMAP['j'] + '   + ' + setcolors(colorstr)[3] + '%s\\n' + COLORMAP['x']\n        DESC_WRAP = COLORMAP['j'] + setcolors(colorstr)[3] + '%s%s' + COLORMAP['x']\n        TAG_STR = COLORMAP['j'] + '   # ' + setcolors(colorstr)[4] + '%s\\n' + COLORMAP['x']\n        TAG_WRAP = COLORMAP['j'] + setcolors(colorstr)[4] + '%s%s' + COLORMAP['x']\n\n        # Enable color in logs\n        setup_logger(LOGGER)\n\n        # Enable prompt with reverse video\n        PROMPTMSG = '\\001\\x1b[7\\002mbuku (? for help)\\001\\x1b[0m\\002 '\n\n    # Enable browser output in case of a text based browser\n    if os.getenv('BROWSER') in TEXT_BROWSERS:\n        browse.suppress_browser_output = False\n    else:\n        browse.suppress_browser_output = True\n\n    # Overriding text browsers is disabled by default\n    browse.override_text_browser = False\n\n    # Handle DB name (--db value without extension and path separators)\n    _db = args.db[0]\n    if _db and not os.path.dirname(_db) and not os.path.splitext(_db)[1]:\n        _db = os.path.join(BukuDb.get_default_dbdir(), _db + '.db')\n\n    # Fallback to prompt if no arguments\n    if args._passed <= {'nostdin', 'db'}:\n        try:\n            _db = _db or os.path.join(BukuDb.get_default_dbdir(), 'bookmarks.db')\n            if not os.path.exists(_db):\n                print(f'DB file is being created at {_db}')  # not printed without chatty param\n            bdb = BukuDb(dbfile=_db, default_scheme=args.default_scheme[0])\n        except Exception:\n            sys.exit(1)\n        prompt(bdb, None)\n        bdb.close_quit(0)\n\n    # Set up debugging\n    if args.debug:\n        LOGGER.setLevel(logging.DEBUG)\n        LOGDBG('buku v%s', __version__)\n        LOGDBG('Python v%s', ('%d.%d.%d' % sys.version_info[:3]))\n    else:\n        logging.disable(logging.WARNING)\n        urllib3.disable_warnings()\n\n    # Handle encrypt/decrypt options at top priority\n    try:\n        if args.lock is not None:\n            BukuCrypt.encrypt_file(args.lock, dbfile=_db)\n            print('File encrypted')\n            sys.exit(0)\n        if args.unlock is not None:\n            BukuCrypt.decrypt_file(args.unlock, dbfile=_db)\n            print('File decrypted')\n    except RuntimeError as e:\n        LOGERR(e)\n        sys.exit(1)\n\n    order = parse_order(args.order or [])\n\n    # Set up title\n    if args.title is not None:\n        title_in = ' '.join(args.title)\n\n    # Set up tags\n    if args.tag is not None:\n        tags_in = args.tag or [DELIM]\n\n    # Set up comment\n    if args.comment is not None:\n        desc_in = ' '.join(args.comment)\n\n    # validating HTTP-code handling args\n    tag_redirect = args.tag_redirect\n    if isinstance(args.tag_redirect, str):\n        try:\n            args.tag_redirect.format(301)\n            tag_redirect = args.tag_redirect.strip() or True\n        except (IndexError, KeyError):\n            LOGERR('Invalid format of --tag-redirect (should use \"{}\" as placeholder)')\n            sys.exit(1)\n    tag_error = args.tag_error\n    if isinstance(args.tag_error, str):\n        try:\n            args.tag_error.format(301)\n            tag_error = args.tag_error.strip() or True\n        except (IndexError, KeyError):\n            LOGERR('Invalid format of --tag-error (should use \"{}\" as placeholder)')\n            sys.exit(1)\n    try:\n        del_error = (None if args.del_error is None else\n                     parse_range(args.del_error, lambda x: 400 <= x < 600) or range(400, 600))\n    except ValueError:\n        LOGERR('Invalid HTTP code(s) given for --del-error (should be within 4xx/5xx ranges)')\n        sys.exit(1)\n    try:\n        _default = (set() if not args.url_redirect and not tag_redirect else PERMANENT_REDIRECTS)\n        _default |= set([] if not tag_error else range(400, 600)) | set(del_error or [])\n        export_on = (None if args.export_on is None else\n                     parse_range(args.export_on, lambda x: 100 <= x < 600) or _default)\n    except ValueError:\n        LOGERR('Invalid HTTP code(s) given for --export-on')\n        sys.exit(1)\n\n    # Initialize the database and get handles, set verbose by default\n    try:\n        bdb = BukuDb(args.json, args.format, not args.tacit, dbfile=_db, colorize=not args.nc, default_scheme=args.default_scheme[0])\n    except Exception:\n        sys.exit(1)\n\n    if args.swap:\n        index1, index2 = args.swap\n        if bdb.swap_recs(index1, index2):\n            bdb.print_rec({index1, index2})\n        else:\n            LOGERR('Failed to swap records #%d and #%d', index1, index2)\n        bdb.close_quit(0)\n\n    # Editor mode\n    if args.write is not None:\n        if not is_editor_valid(args.write):\n            bdb.close_quit(1)\n\n        if is_int(args.write):\n            if not bdb.edit_update_rec(int(args.write), _immutable(args)):\n                bdb.close_quit(1)\n        elif args.add is None:\n            # Edit and add a new bookmark\n            # Parse tags into a comma-separated string\n            tags = parse_tags(tags_in, edit_input=True)\n\n            result = edit_rec(args.write, '', title_in, tags, desc_in)\n            if result is not None:\n                url, title_in, tags, desc_in = result\n                if args.suggest:\n                    tags = bdb.suggest_similar_tag(tags)\n                bdb.add_rec(url, title_in, tags, desc_in, _immutable(args), False, not args.offline)\n\n    # Add record\n    if args.add is not None:\n        if args.url is not None and args.update is None:\n            LOGERR('Bookmark a single URL at a time')\n            bdb.close_quit(1)\n\n        # Parse tags into a comma-separated string\n        # --add may have URL followed by tags\n        keywords_except, keywords = [], args.add[1:]\n        # taglists are taken from --add (starting from 2nd value) and from --tags\n        # if taglist starts with '-', its contents are excluded from resulting tags\n        # if BOTH taglists is are either empty or start with '+'/'-', fetched tags are included\n        if keywords and keywords[0] == '-':\n            keywords, keywords_except = [], keywords[1:]\n        tags_add = (not keywords or keywords[0] == '+')\n        if tags_add:\n            keywords = keywords[1:]\n        if tags_in:\n            # note: need to add a delimiter as url+tags may not end with one\n            if tags_in[0] == '-':\n                keywords_except += [DELIM] + tags_in[1:]\n            elif tags_in[0] == '+':\n                keywords += [DELIM] + tags_in[1:]\n            else:\n                keywords += [DELIM] + tags_in\n                tags_add = False\n\n        tags, tags_except = parse_tags(keywords), parse_tags(keywords_except)\n        tags, tags_except = ((s if s and s != DELIM else None) for s in [tags, tags_except])\n        url = args.add[0]\n        edit_aborted = False\n\n        if args.write and not is_int(args.write):\n            result = edit_rec(args.write, url, title_in, tags, desc_in)\n            if result is not None:\n                url, title_in, tags, desc_in = result\n            else:\n                edit_aborted = True\n\n        if edit_aborted is False:\n            if args.suggest:\n                tags = bdb.suggest_similar_tag(tags)\n            network_test = args.url_redirect or tag_redirect or tag_error or del_error\n            fetch = not args.offline and (network_test or tags_add or title_in is None)\n            bdb.add_rec(url, title_in, tags, desc_in, _immutable(args), delay_commit=False, fetch=fetch,\n                        tags_fetch=tags_add, tags_except=tags_except, url_redirect=args.url_redirect,\n                        tag_redirect=tag_redirect, tag_error=tag_error, del_error=del_error)\n\n    # Search record\n    search_results, search_opted = None, True\n\n    if args.sany is not None:\n        if not args.sany:\n            LOGERR('no keyword')\n        else:\n            LOGDBG('args.sany')\n            # Apply tag filtering, if opted\n            search_results = bdb.search_keywords_and_filter_by_tags(\n                args.sany, deep=args.deep, stag=args.stag, markers=args.markers, without=args.exclude, order=order)\n    elif args.sall is not None:\n        if not args.sall:\n            LOGERR('no keyword')\n        else:\n            LOGDBG('args.sall')\n            search_results = bdb.search_keywords_and_filter_by_tags(\n                args.sall, all_keywords=True, deep=args.deep, stag=args.stag,\n                markers=args.markers, without=args.exclude, order=order)\n    elif args.sreg is not None:\n        if not args.sreg:\n            LOGERR('no expression')\n        else:\n            LOGDBG('args.sreg')\n            search_results = bdb.search_keywords_and_filter_by_tags(\n                args.sreg, regex=True, stag=args.stag, markers=args.markers, without=args.exclude, order=order)\n    elif args.keywords:\n        LOGDBG('args.keywords')\n        search_results = bdb.search_keywords_and_filter_by_tags(\n            args.keywords, deep=args.deep, stag=args.stag, markers=args.markers, without=args.exclude, order=order)\n    elif args.stag is not None:\n        if not args.stag:  # use sub-prompt to list all tags\n            prompt(bdb, None, noninteractive=args.np, listtags=True, suggest=args.suggest, order=order)\n        else:\n            LOGDBG('args.stag')\n            search_results = bdb.exclude_results_from_search(\n                bdb.search_by_tag(' '.join(args.stag), order=order), args.exclude, deep=args.deep, markers=args.markers)\n    elif args.exclude is not None:\n        LOGERR('No search criteria to exclude results from')\n    elif args.markers:\n        LOGERR('No search criteria to apply markers to')\n    else:\n        search_opted = False\n\n    # Add cmdline search options to readline history\n    if search_opted and len(args.keywords):\n        try:\n            readline.add_history(' '.join(args.keywords))\n        except Exception:\n            pass\n\n    check_stdout_encoding()\n    monkeypatch_textwrap_for_cjk()\n\n    update_search_results = False\n    if search_results:\n        if args.random and args.random < len(search_results):\n            search_results = bdb._sort(random.sample(search_results, args.random), order)\n        single_record = args.random == 1  # matching print_rec() behaviour\n\n        oneshot = args.np\n\n        # Open all results in browser right away if args.oa\n        # is specified. The has priority over delete/update.\n        # URLs are opened first and updated/deleted later.\n        if args.oa:\n            for row in search_results:\n                browse(row[1], args.default_scheme[0])\n\n        if (\n                (args.export is not None) or\n                (args.delete is not None and not args.delete) or\n                (args.update is not None and not args.update)):\n            oneshot = True\n\n        if args.json is None and not args.format and not args.random:\n            num = 10 if not args.count else args.count\n            prompt(bdb, search_results, noninteractive=oneshot, deep=args.deep, markers=args.markers, order=order, num=num)\n        elif args.json is None:\n            print_rec_with_filter(search_results, field_filter=args.format)\n        elif args.json:\n            write_string_to_file(format_json(search_results, single_record, field_filter=args.format), args.json)\n        else:\n            # Printing in JSON format is non-interactive\n            print_json_safe(search_results, single_record, field_filter=args.format)\n\n        # Export the results, if opted\n        if args.export and not (args.update is not None and export_on):\n            bdb.exportdb(args.export[0], search_results)\n\n        # In case of search and delete/update,\n        # prompt should be non-interactive\n        # delete gets priority over update\n        if args.delete is not None and not args.delete:\n            bdb.delete_resultset(search_results, retain_order=args.retain_order)\n        elif args.update is not None and not args.update:\n            update_search_results = True\n\n    # Update record\n    if args.update is not None:\n        url_in = (args.url[0] if args.url else None)\n\n        # Parse tags into a comma-separated string\n        tags = parse_tags(tags_in, edit_input=True)\n        tags = (None if tags == DELIM else tags)\n\n        # No arguments to --update, update all\n        if not args.update:\n            # Update all records only if search was not opted\n            if not search_opted:\n                _indices = []\n            elif search_results and update_search_results:\n                if not args.tacit:\n                    print('Updated results:\\n')\n\n                _indices = [x.id for x in search_results]\n            else:\n                _indices = None\n        else:\n            try:\n                _indices = parse_range(args.update, lambda x: x >= 0)\n            except ValueError:\n                LOGERR('Invalid index or range to update')\n                bdb.close_quit(1)\n            _indices = ([] if 0 in _indices else _indices)\n        if _indices is not None:\n            bdb.update_rec(_indices, url_in, title_in, tags, desc_in, _immutable(args), threads=args.threads,\n                           url_redirect=args.url_redirect, tag_redirect=tag_redirect, tag_error=tag_error,\n                           del_error=del_error, export_on=export_on, retain_order=args.retain_order)\n            if args.export and bdb._to_export is not None:\n                bdb.exportdb(args.export[0], order=order)\n\n    # Delete record\n    if args.delete is not None:\n        if not args.delete:\n            # Attempt delete-all only if search was not opted\n            if not search_opted:\n                bdb.cleardb()\n        elif len(args.delete) == 1 and '-' in args.delete[0]:\n            try:\n                vals = [int(x) for x in args.delete[0].split('-')]\n                if len(vals) == 2:\n                    bdb.delete_rec(0, vals[0], vals[1], is_range=True, retain_order=args.retain_order)\n            except ValueError:\n                LOGERR('Invalid index or range to delete')\n                bdb.close_quit(1)\n        else:\n            ids = set(args.delete)\n            try:\n                # Index delete order - highest to lowest\n                ids = sorted(map(int, ids), reverse=True)\n                for idx in ids:\n                    bdb.delete_rec(idx, retain_order=args.retain_order)\n            except ValueError:\n                LOGERR('Invalid index or range or combination')\n                bdb.close_quit(1)\n\n    # Print record\n    if args.print is not None:\n        try:\n            max_id = bdb.get_max_id() or 0\n            id_range = list(parse_range(args.print, maxidx=max_id) or []) or range(1, 1 + max_id)\n        except ValueError:\n            LOGERR('Invalid index or range to print')\n            bdb.close_quit(1)\n        if args.random and args.random < len(id_range):\n            bdb.print_rec(random.sample(id_range, args.random), order=order)\n        elif not args.print:\n            if args.count:\n                search_results = bdb.list_using_id(order=order)\n                prompt(bdb, search_results, noninteractive=args.np, num=args.count, order=order)\n            else:\n                bdb.print_rec(None, order=order)\n        else:\n            if args.count:\n                search_results = bdb.list_using_id(args.print, order=order)\n                prompt(bdb, search_results, noninteractive=args.np, num=args.count, order=order)\n            else:\n                bdb.print_rec(id_range, order=order)\n\n    # Replace a tag in DB\n    if args.replace is not None:\n        if len(args.replace) == 1:\n            bdb.delete_tag_at_index(0, args.replace[0])\n        else:\n            try:\n                bdb.replace_tag(args.replace[0], [' '.join(args.replace[1:])])\n            except Exception as e:\n                LOGERR(str(e))\n                bdb.close_quit(1)\n\n    # Export bookmarks\n    if args.export and not search_opted and not export_on:\n        bdb.exportdb(args.export[0], order=order, pick=args.random)\n\n    # Import bookmarks\n    if args.importfile is not None:\n        bdb.importdb(args.importfile[0], args.tacit)\n\n    # Import bookmarks from browser\n    if args.ai:\n        bdb.auto_import_from_browser(firefox_profile=os.environ.get('FIREFOX_PROFILE'))\n\n    # Open URL in browser\n    if args.open is not None:\n        if not args.open:\n            bdb.browse_by_index(0)\n        else:\n            try:\n                for idx in args.open:\n                    if is_int(idx):\n                        bdb.browse_by_index(int(idx))\n                    elif '-' in idx:\n                        vals = [int(x) for x in idx.split('-')]\n                        bdb.browse_by_index(0, vals[0], vals[-1], True)\n            except ValueError:\n                LOGERR('Invalid index or range to open')\n                bdb.close_quit(1)\n\n    # Try to fetch URL from Wayback Machine\n    if args.cached:\n        wbu = bdb.browse_cached_url(args.cached[0])\n        if wbu is not None:\n            browse(wbu)\n\n    # Report upstream version\n    if args.upstream:\n        check_upstream_release()\n\n    # Fix tags\n    if args.fixtags:\n        bdb.fixtags()\n\n    if args.reorder:\n        size = bdb.get_max_id()\n        if size and (args.np or read_in(f'Are you sure you want to reorder all {size} bookmarks? (y/n): ') == 'y'):\n            bdb.reorder(parse_order(args.reorder))\n            print(f'...Reordered {size} bookmarks.')\n\n    # Close DB connection and quit\n    bdb.close_quit(0)\n\nif __name__ == '__main__':\n    main()\n"
  },
  {
    "path": "bukuserver/README.md",
    "content": "## Bukuserver\n\n_**Note: see [the runner script](https://github.com/jarun/buku/wiki/Bukuserver-%28WebUI%29#runner-script) for advanced installation/running/DB swapping functionality**_\n\n### Table of Contents\n\n- [Installation](#installation)\n  - [Dependencies](#dependencies)\n  - [From PyPi](#from-pypi)\n  - [From source](#from-source)\n  - [Using Docker](#using-docker)\n  - [Using Docker Compose](#using-docker-compose)\n- [Webserver options](#webserver-options)\n- [Configuration](#configuration)\n- [API](#api)\n- [Screenshots](#screenshots)\n\n### Installation\n\nYou need to have some packages before you install `bukuserver` on your server.\nSo be sure to have `python3`, `python3-pip` , `python3-dev`, `libffi-dev` packages from your distribution.\n\n#### Dependencies\n\n```\n$ # venv activation (for development)\n$ python3 -m venv venv\n$ source venv/bin/activate\n$ pip install --upgrade pip\n```\n\n#### From PyPi\n\n```sh\n$ # regular/venv install\n$ pip3 install \"buku[server]\"\n$ # pipx install\n$ pipx install \"buku[server]\"\n$ # with locales\n$ pipx install \"buku[server,locales]\"\n```\n\n#### From source\n\n```sh\n$ git clone https://github.com/jarun/buku\n$ cd buku\n$ # regular/venv install\n$ pip3 install \".[server]\"\n$ # with locales\n$ pip3 install \".[server,locales]\"\n```\n\n#### Using Docker\n\nTo build the image execute the command from the root directory of the project:\n\n```sh\ndocker build -t bukuserver .\n```\n\nTo run the generated image.\n\n```sh\ndocker run -it --rm -v ~/.local/share/buku:/root/.local/share/buku -p 5001:5001 bukuserver\n```\n\nAll the data generated will be stored in the `~/.local/share/buku` directory.\nFeel free to change it to the full path of the location you want to store the\ndatabase.\n\nVisit `127.0.0.1:5001` in your browser to access your bookmarks.\n\n#### Using Docker Compose\n\nThere is a `docker-compose.yml` file present in the `docker-compose` directory\nin the root of this project. You may modify the configurations in this file to\nyour liking, and then simply execute the below command.\n\n```sh\ndocker-compose up -d\n```\n\nYou will have you bukuserver running on port port 80 of the host.\n\nTo stop simply run\n\n```sh\ndocker-compose down\n```\n\nIn case you want to add basic auth to your hosted instance you may do so by\ncreating a `.htpasswd` file in the `data/basic_auth` directory. Add a user to\nthe file using\n\n```sh\nhtpasswd -c data/basic_auth/.htpasswd your_username\n```\n\nAnd then comment out the basic auth lines from the `data/nginx/nginx.conf` file.\n\nFor more information please refer the [nginx docs](https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/).\n\n### Webserver options\n\nTo run the server on host 127.0.0.1, port 5001, run following command:\n\n    $ bukuserver run --host 127.0.0.1 --port 5001\n\nVisit `127.0.0.1:5001` in your browser to access your bookmarks.\n\nSee more option on `bukuserver run --help` and `bukuserver --help`\n\n### Configuration\n\nThe following are [os env config variables](#how-to-specify-environment-variables) available for bukuserver.\n\n_**Important:** all of them have a shared prefix_ **`BUKUSERVER_`**.\n\n| Name (_without prefix_) | Description | Value _²_ |\n| --- | --- | --- |\n| PER_PAGE | bookmarks per page | positive integer [default: 10] |\n| SECRET_KEY | [flask secret key](https://flask.palletsprojects.com/config/#SECRET_KEY) | string [default: random value] |\n| URL_RENDER_MODE | url render mode | `full`, `netloc` or `netloc-tag` [default: `full`] |\n| DB_FILE | full path to db file<em>³</em> | path string [default: standard path for buku] |\n| READONLY | read-only mode | boolean<em>¹</em> [default: `false`] |\n| DISABLE_FAVICON | disable bookmark [favicons](https://wikipedia.org/wiki/Favicon) | boolean<em>¹</em> [default: `true`] ([here's why](#why-favicons-are-disabled-by-default))|\n| AUTOFETCH | initial Fetch value in Create form | boolean<em>¹</em> [default: `true`] |\n| OPEN_IN_NEW_TAB | url link open in new tab | boolean<em>¹</em> [default: `false`] |\n| REVERSE_PROXY_PATH | reverse proxy path<em>⁵</em> | string |\n| SERVER_NAME | canonical host:port for URL generation<em>⁶</em> | string (e.g. `example.com:443`) |\n| THEME | [GUI theme](https://bootswatch.com/4) | string [default: `default`] (`slate` is a good pick for dark mode) |\n| LOCALE | GUI language<em>⁴</em> (partial support) | string [default: `en`] |\n| DEBUG | debug mode (verbose logging etc.) | boolean<em>¹</em> [default: `false`] |\n\n_**¹**_ valid boolean values are `true`, `false`, `1`, `0` (case-insensitive).\n\n_**²**_ if input is invalid, the default value will be used if defined\n\n_**³**_ `BUKUSERVER_DB_FILE` can be a DB name (plain filename without extension; cannot contain `.`). The specified DB with `.db` extension is located in default DB directory (which you can override with `BUKU_DEFAULT_DBDIR`).\n\n_**⁴**_ `BUKUSERVER_LOCALE` requires buku to be installed with `[locales]`\n\n_**⁵**_ the value for `BUKUSERVER_REVERSE_PROXY_PATH` is recommended to include preceding slash and not have trailing slash (i.e. use `/foo` not `/foo/`)\n\n_**⁶**_ `BUKUSERVER_SERVER_NAME` mitigates [Host header injection](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/17-Testing_for_Host_Header_Injection). When set, absolute URLs (e.g. bookmarklet) use this value instead of the client-supplied Host. **Recommended for production and reverse proxy deployments.**\n\n\n#### How to specify environment variables\n\nE.g. to set bukuserver to show 100 items per page run the following command:\n```\n# on linux\n$ export BUKUSERVER_PER_PAGE=100\n\n# on windows\n$ SET BUKUSERVER_PER_PAGE=100\n\n# in dockerfile\nENV BUKUSERVER_PER_PAGE=100\n\n# in env file\nBUKUSERVER_PER_PAGE=100\n```\n\nNote: an env file can be supplied either [by providing `--env-file` CLI argument](https://flask.palletsprojects.com/en/stable/cli/#environment-variables-from-dotenv) (requires `python-dotenv` installed),\nor as config for [the runner script](https://github.com/jarun/buku/wiki/Bukuserver-%28WebUI%29#runner-script).\n\n\n#### Why favicons are disabled by default\n\nAt Bukuserver, we have [disabled favicon as a default setting](#configuration) in order to prevent any non-user triggered network activity.\n\nOur favicon is generated with the assistance of Google.\n\nIt is important to be aware that favicon has the potential to be used for browser fingerprinting,\na technique used to identify and track a person's web browsing habits.\n\n- [Github repo example supercookie](https://github.com/jonasstrehle/supercookie)\n- [Paper by Scientists at University of Illinois, Chicago](https://www.cs.uic.edu/~polakis/papers/solomos-ndss21.pdf)\n- [Article published in 2021 at Heise Online](https://heise.de/-5027814)\n  ([English translation](https://www-heise-de.translate.goog/news/Browser-Fingerprinting-Favicons-als-Super-Cookies-5027814.html?_x_tr_sl=de&_x_tr_tl=en&_x_tr_hl=en))\n\nIt is important to note that favicon can potentially be exploited in this way.\n\n### API\n\nBukuserver implements a RESTful API that provides an HTTP-based interface for the main functionality.\n\nThe API root is at `/api`; you can also access a [Swagger](https://swagger.io/tools/swagger-ui)-based interactive doc at the endpoint `/apidocs` (e.g. `http://localhost:5000/apidocs`).\n\n_**Note: unlike regular IDs, indices aren't static; they're likely to change if used with ID**_\n* `/api/tags` can be used to `GET` the list of all tags\n* `/api/tags/{tag}` can be used to `GET` information on specified tag, as well as `DELETE` or replace it with new tags (`PUT`) in all bookmarks\n* `/api/bookmarks` can be used to `GET` or `DELETE` all bookmarks, as well as create (`POST`) a new one\n* `/api/bookmarks/{index}` can be used to `GET`, `DELETE` or update (`PUT`) an existing bookmark\n* `/api/bookmarks/{start_index}/{end_index}` can be used to `GET`, `DELETE` or update (`PUT`) bookmarks in existing index range\n* `/api/bookmarks/search` can be used to `GET` or `DELETE` bookmarks matching the query (you can use it to obtain current index by URL)\n* ~~`/api/bookmarks/{index}/tiny` can be used to `GET` a shortened URL~~ ([the service providing this functionality is no longer available](https://web.archive.org/web/20250109212915/https://tny.im/))\n* `/api/bookmarks/{index}/refresh` can be used to update (`POST`) data for a bookmark by remotely fetching & parsing the URL\n* `/api/bookmarks/refresh` can be used to update (`POST`) data for _**all**_ bookmarks by remotely fetching & parsing the URLs\n* `/api/fetch_data` can be used to invoke (`POST`) the fetch+parse functionality for an arbitrary URL\n* `/api/network_handle` can be used to invoke (`POST`) the fetch+parse functionality for an arbitrary URL (_outdated interface_)\n\nAlso note that certain `POST`/`DELETE` endpoints (bookmarks search & data fetch) expect their parameters in urlencoded format, while others expect JSON.\n\n### Screenshots\n\n_**Note: more screenshots (to show off `default` & `slate` themes) can be found [on the respective project wiki page](https://github.com/jarun/buku/wiki/Bukuserver-%28WebUI%29)**_\n\n<p><br></p>\n<p align=\"center\">\n  <a href=\"https://github.com/Buku-dev/docs/blob/v5.0-bootstrap4/bukuserver/home-page.png?raw=true\">\n    <img src=\"https://github.com/Buku-dev/docs/blob/v5.0-bootstrap4/bukuserver/home-page.png\" alt=\"home page\" width=\"650\"/>\n  </a>\n</p>\n<p align=\"center\"><i>home page</i></p>\n\n<p><br><br></p>\n<p align=\"center\">\n  <a href=\"https://github.com/Buku-dev/docs/blob/v5.0-bootstrap4/bukuserver/bookmark-stats.png?raw=true\">\n    <img src=\"https://github.com/Buku-dev/docs/blob/v5.0-bootstrap4/bukuserver/bookmark-stats.png\" alt=\"bookmark stats\" width=\"650\"/>\n  </a>\n</p>\n<p align=\"center\"><i>bookmark stats</i></p>\n\n<p><br><br></p>\n<p align=\"center\">\n  <a href=\"https://github.com/Buku-dev/docs/blob/v5.0-bootstrap4/bukuserver/bookmark-page-with-favicon-enabled.png?raw=true\">\n    <img src=\"https://github.com/Buku-dev/docs/blob/v5.0-bootstrap4/bukuserver/bookmark-page-with-favicon-enabled.png\"\n          alt=\"bookmark page with favicon enabled and 'netloc-tag' URL render mode\" width=\"650\"/>\n  </a>\n</p>\n<p align=\"center\"><i>bookmark page <a href=\"#configuration\">with favicon enabled and 'netloc-tag' URL render mode</a></i></p>\n\n<p><br><br></p>\n<p align=\"center\">\n  <a href=\"https://github.com/Buku-dev/docs/blob/v5.0-bootstrap4/bukuserver/bookmark-page-with-slate-theme-and-favicon-enabled.png?raw=true\">\n    <img src=\"https://github.com/Buku-dev/docs/blob/v5.0-bootstrap4/bukuserver/bookmark-page-with-slate-theme-and-favicon-enabled.png\"\n         alt=\"bookmark page with 'slate' theme and favicon enabled\" width=\"650\"/>\n  </a>\n</p>\n<p align=\"center\"><i>bookmark page with 'slate' theme, favicon enabled and 'netloc-tag' URL render mode</i></p>\n\n<p><br><br></p>\n<p align=\"center\">\n  <a href=\"https://github.com/Buku-dev/docs/blob/v5.0-bootstrap4/bukuserver/create-bookmark.png?raw=true\">\n    <img src=\"https://github.com/Buku-dev/docs/blob/v5.0-bootstrap4/bukuserver/create-bookmark.png\" alt=\"create bookmark\" width=\"650\"/>\n  </a>\n</p>\n<p align=\"center\"><i>create bookmark</i></p>\n\n<p><br><br></p>\n<p align=\"center\">\n  <a href=\"https://github.com/Buku-dev/docs/blob/v5.0-bootstrap4/bukuserver/edit-bookmark.png?raw=true\">\n    <img src=\"https://github.com/Buku-dev/docs/blob/v5.0-bootstrap4/bukuserver/edit-bookmark.png\" alt=\"edit bookmark\" width=\"650\"/>\n  </a>\n</p>\n<p align=\"center\"><i>edit bookmark</i></p>\n\n<p><br><br></p>\n<p align=\"center\">\n  <a href=\"https://github.com/Buku-dev/docs/blob/v5.0-bootstrap4/bukuserver/view-bookmark-details.png?raw=true\">\n    <img src=\"https://github.com/Buku-dev/docs/blob/v5.0-bootstrap4/bukuserver/view-bookmark-details.png\" alt=\"view bookmark details\" width=\"650\"/>\n  </a>\n</p>\n<p align=\"center\"><i>view bookmark details</i></p>\n\n<p><br><br></p>\n<p align=\"center\">\n  <a href=\"https://github.com/Buku-dev/docs/blob/v5.0-bootstrap4/bukuserver/tag-page.png?raw=true\">\n    <img src=\"https://github.com/Buku-dev/docs/blob/v5.0-bootstrap4/bukuserver/tag-page.png\" alt=\"tag page\" width=\"650\"/>\n  </a>\n</p>\n<p align=\"center\"><i>tag page</i></p>\n\n<p><br><br></p>\n<p align=\"center\">\n  <a href=\"https://github.com/Buku-dev/docs/blob/v4.10/bukuserver/apidocs.png?raw=true\">\n    <img src=\"https://github.com/Buku-dev/docs/blob/v4.10/bukuserver/apidocs.png\" alt=\"interactive API documentation\" width=\"650\"/>\n  </a>\n</p>\n<p align=\"center\"><i>interactive <a href=\"#api\">API</a> documentation</i></p>\n"
  },
  {
    "path": "bukuserver/__init__.py",
    "content": "try:\n    from flask_babel import gettext, ngettext, pgettext, lazy_gettext, lazy_pgettext, LazyString\nexcept ImportError:\n    from flask_admin.babel import gettext as _gettext, ngettext, lazy_gettext\n    gettext = lambda s, *a, **kw: (s if not kw else _gettext(s, *a, **kw))\n    pgettext = lambda ctx, s, *a, **kw: gettext(s, *a, **kw)\n    lazy_pgettext = lambda ctx, s, *a, **kw: lazy_gettext(s, *a, **kw)\n    LazyString = lambda func, *args, **kwargs: func(*args, **kwargs)\n\n_, _p, _l, _lp = gettext, pgettext, lazy_gettext, lazy_pgettext\n\ndef _key(s):  # replicates ad-hoc implementation of \"get key from lazy string\" used in flask-admin\n    try:\n        return s._args[0]  # works with _/_l, but not with _lp due to the extra context argument\n    except Exception:\n        return str(s)\n\n__all__ = ['_', '_p', '_l', '_lp', '_key', 'gettext', 'pgettext', 'ngettext', 'lazy_gettext', 'lazy_pgettext', 'LazyString']\n"
  },
  {
    "path": "bukuserver/__main__.py",
    "content": "try:\n    from . import server\nexcept ImportError:\n    from bukuserver import server\n\n\nif __name__ == '__main__':\n    server.cli()\n"
  },
  {
    "path": "bukuserver/api.py",
    "content": "#!/usr/bin/env python\n# pylint: disable=wrong-import-order, ungrouped-imports\n\"\"\"Server module.\"\"\"\nimport collections\nimport typing as T\nfrom contextlib import contextmanager\n\nimport flask\nfrom flask import current_app, redirect, request, url_for\nfrom flask.views import MethodView\nfrom werkzeug.exceptions import BadRequest\nfrom flasgger import swag_from\n\nimport buku\nfrom buku import BukuDb\n\ntry:\n    from . import _\n    from response import Response\n    from forms import (TAG_RE, ApiBookmarkCreateForm, ApiBookmarkEditForm, ApiBookmarkRangeEditForm,\n                       ApiBookmarkSearchForm, ApiTagForm, ApiFetchDataForm, ApiBookmarksReorderForm)\nexcept ImportError:\n    from bukuserver import _\n    from bukuserver.response import Response\n    from bukuserver.forms import (TAG_RE, ApiBookmarkCreateForm, ApiBookmarkEditForm, ApiBookmarkRangeEditForm,\n                                  ApiBookmarkSearchForm, ApiTagForm, ApiFetchDataForm, ApiBookmarksReorderForm)\n\n\n_parse_bool = lambda x: str(x).lower() == 'true'\n\ndef entity(bookmark, index=False):\n    data = {\n        'index': bookmark.id,\n        'url': bookmark.url,\n        'title': bookmark.title,\n        'tags': bookmark.taglist,\n        'description': bookmark.desc,\n    }\n    if not index:\n        data.pop('index')\n    return data\n\n\n@contextmanager\ndef get_bukudb():\n    \"\"\"get bukudb instance\"\"\"\n    bukudb = getattr(flask.g, 'bukudb', None)\n    if bukudb:\n        yield bukudb\n    else:\n        db_file = current_app.config.get('BUKUSERVER_DB_FILE', None)\n        bukudb = BukuDb(dbfile=db_file)\n        yield bukudb\n        bukudb.close()\n\n\ndef search_tag(\n    db: BukuDb, stag: T.Optional[str] = None, limit: T.Optional[int] = None\n) -> T.Tuple[T.List[str], T.Dict[str, int]]:\n    \"\"\"search tag.\n\n    db:\n        buku db instance\n    stag:\n        search tag\n    limit:\n        positive integer limit\n\n    Returns\n    -------\n    tuple\n        list of unique tags sorted alphabetically and dictionary of tag and its usage count\n\n    Raises\n    ------\n    ValueError\n        if limit is not positive\n    \"\"\"\n    if limit is not None and limit < 1:\n        raise ValueError(\"limit must be positive\")\n    tags: T.Set[str] = set()\n    counter = collections.Counter()\n    query_list = [\"SELECT DISTINCT tags , COUNT(tags) FROM bookmarks\"]\n    if stag:\n        query_list.append(\"where tags LIKE :search_tag\")\n    query_list.append(\"GROUP BY tags\")\n    row: T.Tuple[str, int]\n    for row in db.cur.execute(\" \".join(query_list), {\"search_tag\": f\"%{stag}%\"}):\n        for tag in row[0].strip(buku.DELIM).split(buku.DELIM):\n            if not tag:\n                continue\n            tags.add(tag)\n            counter[tag] += row[1]\n    return list(sorted(tags)), dict(counter.most_common(limit))\n\n\ndef _fetch_data(convert):\n    form = ApiFetchDataForm(request.form)\n    if not form.validate():\n        return Response.INPUT_NOT_VALID(data={'errors': form.errors})\n    try:\n        return Response.SUCCESS(data=convert(buku.fetch_data(form.url.data)))\n    except Exception as e:\n        current_app.logger.debug(str(e))\n        return Response.FAILURE()\n\n@swag_from('./apidocs/network_handle/post.yml')\ndef handle_network():\n    return _fetch_data(lambda x: {'title': x.title, 'description': x.desc, 'tags': x.keywords,\n                                  'recognized mime': int(x.mime), 'bad url': int(x.bad)})\n\n@swag_from('./apidocs/fetch_data/post.yml')\ndef fetch_data():\n    return _fetch_data(lambda x: x._asdict())\n\n\n@swag_from('./apidocs/bookmarks_refresh/post.yml', endpoint='bookmarks_refresh')\n@swag_from('./apidocs/bookmark_refresh/post.yml', endpoint='bookmark_refresh')\ndef refresh_bookmark(index: T.Optional[int]):\n    with get_bukudb() as bdb:\n        if index and not bdb.get_rec_by_id(index):\n            return Response.BOOKMARK_NOT_FOUND()\n        result_flag = bdb.refreshdb(index or None, request.form.get('threads', 4))\n        return Response.from_flag(result_flag)\n\n@swag_from('./apidocs/bookmarks_reorder/post.yml')\ndef reorder_bookmarks():\n    try:\n        form = ApiBookmarksReorderForm(data=request.get_json())\n    except BadRequest:\n        return Response.INVALID_REQUEST()\n    if not form.validate():\n        return Response.invalid(form.errors)\n    try:\n        with get_bukudb() as bdb:\n            bdb.reorder(form.order.data)\n        return Response.SUCCESS()\n    except Exception as e:\n        current_app.logger.exception(str(e))\n        return Response.FAILURE()\n\n\nget_tiny_url = swag_from('./apidocs/tiny_url/get.yml')(lambda index: Response.REMOVED())\n\n\n@swag_from('./apidocs/tags/get.yml')\ndef get_all_tags():\n    with get_bukudb() as bdb:\n        return Response.SUCCESS(data={\"tags\": search_tag(db=bdb, limit=5)[0]})\n\nclass ApiTagView(MethodView):\n    def get(self, tag: str):\n        with get_bukudb() as bukudb:\n            if not TAG_RE.match(tag):\n                return Response.TAG_NOT_VALID()\n            tag = tag.lower().strip()\n            tags = search_tag(db=bukudb, stag=tag)\n            return (Response.TAG_NOT_FOUND() if tag not in tags[1] else\n                    Response.SUCCESS(data={\"name\": tag, \"usage_count\": tags[1][tag]}))\n\n    def put(self, tag: str):\n        if not TAG_RE.match(tag):\n            return Response.TAG_NOT_VALID()\n        try:\n            form = ApiTagForm(data=request.get_json())\n        except BadRequest:\n            return Response.INVALID_REQUEST()\n        if not form.validate():\n            return Response.invalid(form.errors)\n        with get_bukudb() as bukudb:\n            tag = tag.lower().strip()\n            tags = search_tag(db=bukudb, stag=tag)\n            if tag not in tags[1]:\n                return Response.TAG_NOT_FOUND()\n            try:\n                bukudb.replace_tag(tag, form.tags.data)\n                return Response.SUCCESS()\n            except (ValueError, RuntimeError):\n                return Response.FAILURE()\n\n    def delete(self, tag: str):\n        if not TAG_RE.match(tag):\n            return Response.TAG_NOT_VALID()\n        with get_bukudb() as bukudb:\n            tag = tag.lower().strip()\n            tags = search_tag(db=bukudb, stag=tag)\n            return (Response.TAG_NOT_FOUND() if tag not in tags[1] else\n                    Response.from_flag(bukudb.delete_tag_at_index(0, tag, chatty=False)))\n\n\nclass ApiBookmarksView(MethodView):\n    def get(self):\n        with get_bukudb() as bukudb:\n            order = request.args.getlist('order')\n            all_bookmarks = bukudb.get_rec_all(order=order)\n            return Response.SUCCESS(data={'bookmarks': [entity(bookmark, index=order)\n                                                        for bookmark in all_bookmarks]})\n\n    def post(self):\n        try:\n            form = ApiBookmarkCreateForm(data=request.get_json())\n        except BadRequest:\n            return Response.INVALID_REQUEST()\n        if not form.validate():\n            return Response.invalid(form.errors)\n        with get_bukudb() as bukudb:\n            index = bukudb.add_rec(\n                form.url.data,\n                form.title.data,\n                form.tags_str,\n                form.description.data,\n                fetch=form.fetch.data)\n            return Response.from_flag(index is not None, data=index and {'index': index})\n\n    def delete(self):\n        with get_bukudb() as bukudb:\n            return Response.from_flag(bukudb.cleardb(confirm=False))\n\n\nclass ApiBookmarkView(MethodView):\n    def get(self, index: int):\n        with get_bukudb() as bukudb:\n            bookmark = bukudb.get_rec_by_id(index)\n            return (Response.BOOKMARK_NOT_FOUND() if bookmark is None else\n                    Response.SUCCESS(data=entity(bookmark)))\n\n    def put(self, index: int):\n        try:\n            form = ApiBookmarkEditForm(data=request.get_json())\n        except BadRequest:\n            return Response.INVALID_REQUEST()\n        if not form.validate():\n            return Response.invalid(form.errors)\n        with get_bukudb() as bukudb:\n            if not bukudb.get_rec_by_id(index):\n                return Response.BOOKMARK_NOT_FOUND()\n            if not form.has_data:\n                return Response.SUCCESS()  # noop\n            success = bukudb.update_rec(index, url=form.url.data, title_in=form.title.data,\n                                        tags_in=form.tags_str, desc=form.description.data)\n            return Response.from_flag(success)\n\n    def delete(self, index: int):\n        with get_bukudb() as bukudb:\n            return (Response.BOOKMARK_NOT_FOUND() if not bukudb.get_rec_by_id(index) else\n                    Response.from_flag(bukudb.delete_rec(index, retain_order=True)))\n\n\nclass ApiBookmarkRangeView(MethodView):\n    def get(self, start_index: int, end_index: int):\n        with get_bukudb() as bukudb:\n            max_index = bukudb.get_max_id() or 0\n            if start_index > end_index or end_index > max_index:\n                return Response.RANGE_NOT_VALID()\n            result = {'bookmarks': {index: entity(bukudb.get_rec_by_id(index))\n                                    for index in range(start_index, end_index + 1)}}\n            return Response.SUCCESS(data=result)\n\n    def put(self, start_index: int, end_index: int):\n        with get_bukudb() as bukudb:\n            max_index = bukudb.get_max_id() or 0\n            if start_index > end_index or end_index > max_index:\n                return Response.RANGE_NOT_VALID()\n            updates = []\n            errors = {}\n            for index in range(start_index, end_index + 1):\n                try:\n                    json = request.get_json().get(str(index))\n                except BadRequest:\n                    return Response.INVALID_REQUEST()\n                if json is None:\n                    errors[index] = _('Input required.')\n                    continue\n                form = ApiBookmarkRangeEditForm(data=json)\n                if not form.validate():\n                    errors[index] = form.errors\n                elif form.has_data:\n                    updates += [{'index': index,\n                                 'url': form.url.data,\n                                 'title_in': form.title.data,\n                                 'tags_in': form.tags_in,\n                                 'desc': form.description.data}]\n            if errors:\n                return Response.invalid(errors)\n            for update in updates:\n                if not bukudb.update_rec(**update):\n                    return Response.FAILURE(data={'index': update['index']})\n            return Response.SUCCESS()\n\n    def delete(self, start_index: int, end_index: int):\n        with get_bukudb() as bukudb:\n            max_index = bukudb.get_max_id() or 0\n            if start_index > end_index or end_index > max_index:\n                return Response.RANGE_NOT_VALID()\n            result_flag = bukudb.delete_rec(None, start_index, end_index, is_range=True, retain_order=True)\n            return Response.from_flag(result_flag)\n\n\nclass ApiBookmarkSearchView(MethodView):\n    def get(self):\n        form = ApiBookmarkSearchForm(request.args)\n        if not form.validate():\n            return Response.INPUT_NOT_VALID(data={'errors': form.errors})\n        with get_bukudb() as bukudb:\n            result = [entity(bookmark, index=True) for bookmark in bukudb.searchdb(**form.data)]\n            current_app.logger.debug('total bookmarks:{}'.format(len(result)))\n            return Response.SUCCESS(data={'bookmarks': result})\n\n    def delete(self):\n        form = ApiBookmarkSearchForm(request.form)\n        if not form.validate():\n            return Response.INPUT_NOT_VALID(data={'errors': form.errors})\n        with get_bukudb() as bukudb:\n            deleted, failed, indices = 0, 0, {x.id for x in bukudb.searchdb(**form.data)}\n            current_app.logger.debug('total bookmarks:{}'.format(len(indices)))\n            for index in sorted(indices, reverse=True):\n                if bukudb.delete_rec(index, retain_order=True):\n                    deleted += 1\n                else:\n                    failed += 1\n            return Response.from_flag(failed == 0, data={'deleted': deleted}, errors={'failed': failed})\n\n\ndef bookmarklet_redirect():\n    url = request.args.get('url')\n    title = request.args.get('title')\n    description = request.args.get('description')\n    tags = request.args.get('tags')\n    fetch = request.args.get('fetch')\n\n    with get_bukudb() as bukudb:\n        rec_id = bukudb.get_rec_id(url)\n        goto = (url_for('bookmark.edit_view', id=rec_id, popup=True) if rec_id else\n                url_for('bookmark.create_view', link=url, title=title, description=description, tags=tags, fetch=fetch, popup=True))\n        return redirect(goto)\n"
  },
  {
    "path": "bukuserver/apidocs/bookmark/delete.yml",
    "content": "Delete an existing bookmark at current index\n---\n#DELETE /api/bookmarks/{index}\n\ntags: [Bookmarks]\n\nparameters:\n  - name: index\n    in: path\n    required: true\n    type: integer\n    minimum: 1\n\nresponses:\n  200:\n    description: Success\n    schema:\n      $ref: '#/definitions/Response:Success'\n\n  404:\n    description: Bookmark not found\n    schema:\n      $ref: '#/definitions/Response:NotFound:Bookmark'\n\n  409:\n    description: Failure\n    schema:\n      $ref: '#/definitions/Response:Failure'\n"
  },
  {
    "path": "bukuserver/apidocs/bookmark/get.yml",
    "content": "Fetch the data of a bookmark at current index\n---\n#GET /api/bookmarks/{index}\n\ntags: [Bookmarks]\n\nparameters:\n  - name: index\n    in: path\n    required: true\n    type: integer\n    minimum: 1\n\nresponses:\n  200:\n    description: Bookmark data\n    schema:\n      allOf:\n        - $ref: '#/definitions/Response:Success'\n        - $ref: '#/definitions/Data:Bookmark'\n\n  404:\n    description: Bookmark not found\n    schema:\n      $ref: '#/definitions/Response:NotFound:Bookmark'\n"
  },
  {
    "path": "bukuserver/apidocs/bookmark/put.yml",
    "content": "Update an existing bookmark at current index\n---\n#PUT /api/bookmarks/{index}  {Bookmark}\n\ntags: [Bookmarks]\n\nparameters:\n  - name: index\n    in: path\n    required: true\n    type: integer\n    minimum: 1\n  - name: form\n    in: body\n    required: true\n    description: \"The URL must not be present in any other bookmark\"\n    schema:\n      $ref: '#/definitions/Form:Bookmark'\n\nresponses:\n  200:\n    description: Updated successfully\n    schema:\n      $ref: '#/definitions/Response:Success'\n\n  400:\n    description: Ill-formed request\n    schema:\n      $ref: '#/definitions/Response:IllFormedRequest'\n\n  404:\n    description: Bookmark not found\n    schema:\n      $ref: '#/definitions/Response:NotFound:Bookmark'\n\n  409:\n    description: Failed to update a bookmark\n    schema:\n      $ref: '#/definitions/Response:Failure'\n\n  422:\n    description: Invalid request data\n    schema:\n      $ref: '#/definitions/Response:InputNotValid:Bookmark'\n"
  },
  {
    "path": "bukuserver/apidocs/bookmark_range/delete.yml",
    "content": "Delete all bookmarks in specified index range\n---\n#DELETE /api/bookmarks/{start_index}/{end_index}\n\ntags: [Bookmarks]\n\nparameters:\n  - name: start_index\n    in: path\n    required: true\n    type: integer\n    minimum: 1\n  - name: end_index\n    in: path\n    required: true\n    type: integer\n    minimum: 1\n\nresponses:\n  200:\n    description: Success\n    schema:\n      $ref: '#/definitions/Response:Success'\n\n  404:\n    description: Range not valid (this includes nonexistent indices)\n    schema:\n      $ref: '#/definitions/Response:NotFound:BookmarkRange'\n\n  409:\n    description: Failure\n    schema:\n      $ref: '#/definitions/Response:Failure'\n"
  },
  {
    "path": "bukuserver/apidocs/bookmark_range/get.yml",
    "content": "Fetch the data of all bookmarks in specified index range\n---\n#GET /api/bookmarks/{start_index}/{end_index}\n\ntags: [Bookmarks]\n\nparameters:\n  - name: start_index\n    in: path\n    required: true\n    type: integer\n    minimum: 1\n  - name: end_index\n    in: path\n    required: true\n    type: integer\n    minimum: 1\n\nresponses:\n  200:\n    description: Bookmark data\n    schema:\n      allOf:\n        - $ref: '#/definitions/Response:Success'\n        - properties:\n            bookmarks:\n              type: object\n              additionalProperties:\n                $ref: '#/definitions/Data:Bookmark'\n              description: \"Each key is the respective bookmark index\"\n              example:\n                {42: {url: \"https://slashdot.org/\",\n                      title: \"SLASHDOT\",\n                      tags: ['news', 'old'],\n                      description: \"News for old nerds, stuff that doesn't matter\"}}\n\n  404:\n    description: Range not valid (this includes nonexistent indices)\n    schema:\n      $ref: '#/definitions/Response:NotFound:BookmarkRange'\n"
  },
  {
    "path": "bukuserver/apidocs/bookmark_range/put.yml",
    "content": "Update all bookmarks in specified index range\n_Note that this request **does not overwrite tags** (instead, tags are added or deleted based on **del_tags** value)._\n---\n#PUT /api/bookmarks/{start_index}/{end_index}  {Bookmark}\n\ntags: [Bookmarks]\n\nparameters:\n  - name: start_index\n    in: path\n    required: true\n    type: integer\n    minimum: 1\n  - name: end_index\n    in: path\n    required: true\n    type: integer\n    minimum: 1\n  - name: form\n    in: body\n    required: true\n    schema:\n      type: object\n      description: \"Each key is the respective bookmark index. _**All**_ bookmarks in the range must be present. (Indices outside of the range are ignored.)\"\n      additionalProperties:\n        $ref: '#/definitions/Form:Bookmark'\n      example: {1: {title: \"SLASHDOT\"}, 2: {}, 3: {tags: ['old', 'news'], del_tags: false}, 4: {fetch: true}}\n\nresponses:\n  200:\n    description: Updated successfully\n    schema:\n      $ref: '#/definitions/Response:Success'\n\n  400:\n    description: Ill-formed request\n    schema:\n      $ref: '#/definitions/Response:IllFormedRequest'\n\n  404:\n    description: Range not valid (this includes nonexistent indices)\n    schema:\n      $ref: '#/definitions/Response:NotFound:BookmarkRange'\n\n  409:\n    description: Failed to update a bookmark\n    schema:\n      allOf:\n        - $ref: '#/definitions/Response:Failure'\n        - properties:\n            index:\n              type: integer\n              description: \"Indicates which bookmark could not be updated.\"\n              example: 42\n\n  422:\n    description: Invalid request data\n    schema:\n      $ref: '#/definitions/Response:InputNotValid:BookmarkRange'\n"
  },
  {
    "path": "bukuserver/apidocs/bookmark_refresh/post.yml",
    "content": "Refresh bookmark data from fetched resource.\n---\n#POST /api/bookmarks/{index}/refresh\n\ntags: [Util]\n\nparameters:\n  - name: index\n    in: path\n    required: true\n    type: integer\n    minimum: 1\n\nresponses:\n  200:\n    description: Success\n    schema:\n      $ref: '#/definitions/Response:Success'\n\n  404:\n    description: Bookmark not found\n    schema:\n      $ref: '#/definitions/Response:NotFound:Bookmark'\n\n  409:\n    description: Failure\n    schema:\n      $ref: '#/definitions/Response:Failure'\n"
  },
  {
    "path": "bukuserver/apidocs/bookmarks/delete.yml",
    "content": "Delete all bookmarks from the database\n---\n#DELETE /api/bookmarks\n\ntags: [Bookmarks]\n\nresponses:\n  200:\n    description: Success\n    schema:\n      $ref: '#/definitions/Response:Success'\n\n  409:\n    description: Failure\n    schema:\n      $ref: '#/definitions/Response:Failure'\n"
  },
  {
    "path": "bukuserver/apidocs/bookmarks/get.yml",
    "content": "Fetch the list of all bookmarks\n---\n#GET /api/bookmarks?order=\n\ntags: [Bookmarks]\n\nparameters:\n  - name: order\n    in: query\n    collectionFormat: multi\n    type: array\n    items:\n      type: string\n    example: [-netloc, title, +url]\n    description: |-\n      Determines ordering of the bookmarks list, by sequentially comparing values of each specified field.\n      Valid field names: `index`, `url` (or `uri`), `title`, `description` (or `desc`), `tags`, `netloc` (i.e. hostname).\n      A field name can be prefixed with `+` or `-` to specify sorting direction for the field (`+` is the default).\n    # omitted some valid names that may be confusing for the user\n\nresponses:\n  200:\n    description: A list of bookmarks (with indices if **order** was supplied)\n    schema:\n      allOf:\n        - $ref: '#/definitions/Response:Success'\n        - properties:\n            bookmarks:\n              type: array\n              items:\n                $ref: '#/definitions/Data:BookmarkWithIndex'\n"
  },
  {
    "path": "bukuserver/apidocs/bookmarks/post.yml",
    "content": "Create a new bookmark\n---\n#POST /api/bookmarks  {Bookmark}\n\ntags: [Bookmarks]\n\nparameters:\n  - name: form\n    in: body\n    required: true\n    description: \"The URL must not be present in the database\"\n    schema:\n      allOf:\n        - $ref: '#/definitions/Form:Bookmark'\n        - properties:\n            url:\n              required: true\n\nresponses:\n  200:\n    description: Success\n    schema:\n      allOf:\n        - $ref: '#/definitions/Response:Success'\n        - properties:\n            index:\n              type: integer\n              example: 42\n\n  400:\n    description: Ill-formed request\n    schema:\n      $ref: '#/definitions/Response:IllFormedRequest'\n\n  409:\n    description: Failed to create a bookmark (e.g. duplicate URL)\n    schema:\n      $ref: '#/definitions/Response:Failure'\n\n  422:\n    description: Invalid request data\n    schema:\n      $ref: '#/definitions/Response:InputNotValid:Bookmark'\n"
  },
  {
    "path": "bukuserver/apidocs/bookmarks_refresh/post.yml",
    "content": "Refresh all bookmark data from fetched resources.\n---\n#POST /api/bookmarks/refresh\n\ntags: [Util]\n\nresponses:\n  200:\n    description: Success\n    schema:\n      $ref: '#/definitions/Response:Success'\n\n  409:\n    description: Failure\n    schema:\n      $ref: '#/definitions/Response:Failure'\n"
  },
  {
    "path": "bukuserver/apidocs/bookmarks_reorder/post.yml",
    "content": "Update DB indices to match specified order.\n---\n#POST /api/bookmarks/reorder  order=\n\ntags: [Util]\n\nparameters:\n  - name: form\n    in: body\n    required: true\n    schema:\n      type: object\n      properties:\n        order:\n          required: true\n          type: array\n          items:\n            type: string\n          example: [-netloc, title, +url]\n          description: |-\n            Determines ordering of the bookmarks list, by sequentially comparing values of each specified field.\n            Valid field names: `index`, `url` (or `uri`), `title`, `description` (or `desc`), `tags`, `netloc` (i.e. hostname).\n            A field name can be prefixed with `+` or `-` to specify sorting direction for the field (`+` is the default).\n          # omitted some valid names that may be confusing for the user\n\nresponses:\n  200:\n    description: Reordered successfully\n    schema:\n      $ref: '#/definitions/Response:Success'\n\n  400:\n    description: Ill-formed request\n    schema:\n      $ref: '#/definitions/Response:IllFormedRequest'\n\n  409:\n    description: Failed to reorder bookmarks\n    schema:\n      $ref: '#/definitions/Response:Failure'\n\n  422:\n    description: Invalid request data\n    schema:\n      $ref: '#/definitions/Response:InputNotValid'\n"
  },
  {
    "path": "bukuserver/apidocs/bookmarks_search/delete.yml",
    "content": "Delete all bookmarks matching the query\nYou can send a non-boolean value in a boolean field, but it is only considered truthy if converting it to a lowercase string produces **'true'**.\n\nSpecial behaviour exists for the following combinations:\n- **all_keywords** = _true_, **regex** = _false_, **keywords** = _blank_ (bookmarks with no title or no tags)\n- **all_keywords** = _true_, **regex** = _false_, **keywords** = _immutable_ (bookmarks marked with **--immutable** flag)\n---\n#GET /api/bookmarks/search  {BookmarkSearch=}\n\ntags: [Util]\nconsumes: ['application/x-www-form-urlencoded']\n\nparameters:\n  - name: all_keywords\n    in: formData\n    type: boolean\n    description: \"Exclude partial matches (with multiple keywords).\"\n  - name: deep\n    in: formData\n    type: boolean\n    description: \"Unless set to true, only _**full** words_ will be matched.\"\n  - name: regex\n    in: formData\n    type: boolean\n    description: \"The keyword(s) are regular expressions (overrides other options).\"\n  - name: markers\n    in: formData\n    type: boolean\n    description: |-\n      When this is enabled, each keyword will be applied to a specific field based on prefix:\n      * keywords starting with `.`, `>` or `:` will be searched for in _title_, _description_ and _URL_ respectively\n      * `#` will be searched for in _tags_ (comma-separated, partial matches; not affected by **deep** value)\n      * `#,` is the same but will match _**full** tags only_\n      * `*` will be searched for in _all fields_ (implied if no prefix was provided)\n\n  - name: keywords\n    in: formData\n    required: true\n    collectionFormat: multi\n    type: array\n    items:\n      type: string\n    example: [\"global substring\", \".title substring\", \":url substring\", \">description substring\", \"#partial,tags:\", \"#,exact,tags\", \"*another global substring\"]\n    description: \"A set of terms to search for.\"\n\n\nresponses:\n  200:\n    description: Deletion successful\n    schema:\n      allOf:\n        - $ref: '#/definitions/Response:Success'\n        - properties:\n            deleted:\n              type: integer\n              description: \"Amount of bookmarks that were successfully deleted.\"\n              example: 3\n\n  409:\n    description: Deletion failed\n    schema:\n      allOf:\n        - $ref: '#/definitions/Response:Failure'\n        - properties:\n            deleted:\n              type: integer\n              description: \"Amount of bookmarks that were successfully deleted.\"\n              example: 1\n            errors:\n              type: object\n              properties:\n                failed:\n                  type: integer\n                  example: 2\n                  description: \"Amount of bookmarks that could not be deleted.\"\n\n  422:\n    description: Invalid request data\n    schema:\n      $ref: '#/definitions/Response:InputNotValid:BookmarkSearch'\n"
  },
  {
    "path": "bukuserver/apidocs/bookmarks_search/get.yml",
    "content": "Fetch all bookmarks matching the query\nYou can send a non-boolean value in a boolean field, but it is only considered truthy if converting it to a lowercase string produces **'true'**.\n\nSpecial behaviour exists for the following combinations:\n- **all_keywords** = _true_, **regex** = _false_, **keywords** = _blank_ (bookmarks with no title or no tags)\n- **all_keywords** = _true_, **regex** = _false_, **keywords** = _immutable_ (bookmarks marked with **--immutable** flag)\n---\n#GET /api/bookmarks/search?{BookmarkSearch=}\n\ntags: [Util]\n\nparameters:\n  - name: all_keywords\n    in: query\n    type: boolean\n    description: \"Exclude partial matches (with multiple keywords).\"\n  - name: deep\n    in: query\n    type: boolean\n    description: \"Unless set to true, only _**full** words_ will be matched.\"\n  - name: regex\n    in: query\n    type: boolean\n    description: \"The keyword(s) are regular expressions (overrides other options).\"\n  - name: order\n    in: query\n    collectionFormat: multi\n    type: array\n    items:\n      type: string\n    example: [-netloc, title, +url]\n    description: |-\n      Determines ordering of the bookmarks list, by sequentially comparing values of each specified field.\n      Valid field names: `index`, `url` (or `uri`), `title`, `description` (or `desc`), `tags`, `netloc` (i.e. hostname).\n      A field name can be prefixed with `+` or `-` to specify sorting direction for the field (`+` is the default).\n    # omitted some valid names that may be confusing for the user\n\n  - name: markers\n    in: query\n    type: boolean\n    description: |-\n      When this is enabled, each keyword will be applied to a specific field based on prefix:\n      * keywords starting with `.`, `>` or `:` will be searched for in _title_, _description_ and _URL_ respectively\n      * `#` will be searched for in _tags_ (comma-separated, partial matches; not affected by **deep** value)\n      * `#,` is the same but will match _**full** tags only_\n      * `*` will be searched for in _all fields_ (implied if no prefix was provided)\n\n  - name: keywords\n    in: query\n    required: true\n    collectionFormat: multi\n    type: array\n    items:\n      type: string\n    example: [\"global substring\", \".title substring\", \":url substring\", \">description substring\", \"#partial,tags:\", \"#,exact,tags\", \"*another global substring\"]\n    description: \"A set of terms to search for.\"\n\nresponses:\n  200:\n    description: A list of bookmarks\n    schema:\n      allOf:\n        - $ref: '#/definitions/Response:Success'\n        - properties:\n            bookmarks:\n              type: array\n              items:\n                $ref: '#/definitions/Data:BookmarkWithIndex'\n\n  422:\n    description: Invalid request data\n    schema:\n      $ref: '#/definitions/Response:InputNotValid:BookmarkSearch'\n"
  },
  {
    "path": "bukuserver/apidocs/fetch_data/post.yml",
    "content": "Fetch data from URL (i.e. to test parsing functionality)\n---\n#POST /api/fetch_data  url=\n\ntags: [Util]\nconsumes: ['application/x-www-form-urlencoded']\n\nparameters:\n  - name: url\n    in: formData\n    required: true\n    type: string\n    format: uri\n\nresponses:\n  200:\n    description: Operation executed normally\n    schema:\n      allOf:\n        - $ref: '#/definitions/Response:Success'\n        - properties:\n            url:\n              type: string\n              format: uri\n              example: 'https://slashdot.org/'\n            title:\n              type: string\n              example: \"Slashdot: News for nerds, stuff that matters\"\n            desc:\n              type: string\n              example: \"Slashdot: News for nerds, stuff that matters.\\n\n                Timely news source for technology related news with a heavy slant towards Linux and Open Source issues.\"\n            keywords:\n              type: string\n              example: \"empty,usually\"\n            mime:\n              type: boolean\n              example: false\n              description: true indicates that the URL implied non-webpage content (and therefore HTTP HEAD request was sent)\n            bad:\n              type: boolean\n              example: false\n              description: true indicates that the input did not contain a fetchable URL (and therefore no request was sent)\n            fetch_status:\n              type: integer\n              example: 200\n              description: HTTP response code or null (e.g. if network error occurred)\n\n  409:\n    description: Operation could not be executed\n    schema:\n      $ref: '#/definitions/Response:Failure'\n\n  422:\n    description: Invalid request data\n    schema:\n      $ref: '#/definitions/Response:InputNotValid:Url'\n"
  },
  {
    "path": "bukuserver/apidocs/network_handle/post.yml",
    "content": "Fetch data from URL (i.e. to test parsing functionality)\n`[DEPRECATED]` prefer **/api/fetch_data**\n---\n#POST /api/network_handle  url=\n\ntags: [Util]\nconsumes: ['application/x-www-form-urlencoded']\n\nparameters:\n  - name: url\n    in: formData\n    required: true\n    type: string\n    format: uri\n\nresponses:\n  200:\n    description: Operation executed normally\n    schema:\n      allOf:\n        - $ref: '#/definitions/Response:Success'\n        - properties:\n            title:\n              type: string\n              example: \"Slashdot: News for nerds, stuff that matters\"\n            description:\n              type: string\n              example: \"Slashdot: News for nerds, stuff that matters.\\n\n                Timely news source for technology related news with a heavy slant towards Linux and Open Source issues.\"\n            tags:\n              type: string\n              example: \"empty,usually\"\n            recognized mime:\n              type: integer\n              example: 0\n              description: 1 indicates that the URL implied non-webpage content (and therefore HTTP HEAD request was sent)\n            bad url:\n              type: integer\n              example: 0\n              description: 1 indicates that the input did not contain a fetchable URL (and therefore no request was sent)\n\n  409:\n    description: Operation could not be executed\n    schema:\n      $ref: '#/definitions/Response:Failure'\n\n  422:\n    description: Invalid request data\n    schema:\n      $ref: '#/definitions/Response:InputNotValid:Url'\n"
  },
  {
    "path": "bukuserver/apidocs/tag/delete.yml",
    "content": "Remove the specified tag from all bookmarks\n---\n#DELETE /api/tags/{tag}\n\ntags: [Tags]\n\nparameters:\n  - name: tag\n    in: path\n    required: true\n    type: string\n    pattern: '^[^,]*[^,\\s]+[^,]*$'\n    description: \"Cannot include comma; cannot be blank\"\n\nresponses:\n  200:\n    description: Deleted successfully\n    schema:\n      $ref: '#/definitions/Response:Success'\n\n  404:\n    description: Tag not found\n    schema:\n      $ref: '#/definitions/Response:NotFound:Tag'\n\n  409:\n    description: Failed to delete\n    schema:\n      $ref: '#/definitions/Response:Failure'\n\n  422:\n    description: Invalid tag\n    schema:\n      $ref: '#/definitions/Response:NotValid:Tag'\n"
  },
  {
    "path": "bukuserver/apidocs/tag/get.yml",
    "content": "Get information on the specified tag\n---\n#GET /api/tags/{tag}\n\ntags: [Tags]\n\nparameters:\n  - name: tag\n    in: path\n    required: true\n    type: string\n    pattern: '^[^,]*[^,\\s]+[^,]*$'\n    description: \"Cannot include comma; cannot be blank\"\n\nresponses:\n  200:\n    description: Tag information\n    schema:\n      allOf:\n        - $ref: '#/definitions/Response:Success'\n        - type: object\n          properties:\n            name:\n              type: string\n              example: 'foo'\n            usage_count:\n              type: integer\n              example: 42\n\n  404:\n    description: Tag not found\n    schema:\n      $ref: '#/definitions/Response:NotFound:Tag'\n\n  422:\n    description: Invalid tag\n    schema:\n      $ref: '#/definitions/Response:NotValid:Tag'\n"
  },
  {
    "path": "bukuserver/apidocs/tag/put.yml",
    "content": "Replace the specified tag with one or more new tags\n---\n#PUT /api/tags/{tag}  {\"tags\": []}\n\ntags: [Tags]\n\nparameters:\n  - name: tag\n    in: path\n    required: true\n    type: string\n    pattern: '^[^,]*[^,\\s]+[^,]*$'\n    description: \"Cannot include comma; cannot be blank\"\n  - name: form\n    in: body\n    required: true\n    schema:\n      $ref: '#/definitions/Form:Tags'\n\nresponses:\n  200:\n    description: Renamed successfully\n    schema:\n      $ref: '#/definitions/Response:Success'\n\n  400:\n    description: Ill-formed request\n    schema:\n      $ref: '#/definitions/Response:IllFormedRequest'\n\n  404:\n    description: Tag not found\n    schema:\n      $ref: '#/definitions/Response:NotFound:Tag'\n\n  409:\n    description: Failed to rename\n    schema:\n      $ref: '#/definitions/Response:Failure'\n\n  422:\n    description: Invalid tag/request data\n    schema:\n      $ref: '#/definitions/Response:InputNotValid:Tags'\n"
  },
  {
    "path": "bukuserver/apidocs/tags/get.yml",
    "content": "Fetch the list of all tags\n---\n#GET /api/tags\n\ntags: [Tags]\n\nresponses:\n  200:\n    description: A list of tags (sorted lexicographically)\n    schema:\n      allOf:\n        - $ref: '#/definitions/Response:Success'\n        - type: object\n          properties:\n            tags:\n              type: array\n              items:\n                type: string\n              example: ['bar', 'baz', 'foo']\n"
  },
  {
    "path": "bukuserver/apidocs/template.yml",
    "content": "info:\n  title: \"Bukuserver API\"\n  description: \"RESTful API for managing your personal bookmarks.\"\n  version: 'v5.0'\n\nconsumes: ['application/json']\nproduces: ['application/json']\n\ntags:\n  - name: Tags\n    description: \"Global operations on tags\"\n  - name: Bookmarks\n    description: \"Index-based (CRUD) operations on bookmarks\"\n  - name: Util\n    description: \"Other functionality\"\n\ndefinitions:\n  'Value:Tag':\n    type: string\n    pattern: '^[^,]*[^,\\s]+[^,]*$'\n    description: \"Cannot include comma; cannot be blank\"\n\n  'Data:Bookmark':\n    type: object\n    properties:\n      url:\n        type: string\n        format: uri\n        example: 'https://slashdot.org/'\n      title:\n        type: string\n        example: \"SLASHDOT\"\n      tags:\n        type: array\n        items:\n          $ref: '#/definitions/Value:Tag'\n        example: ['news', 'old']\n      description:\n        type: string\n        example: \"News for old nerds, stuff that doesn't matter\"\n\n  'Data:BookmarkWithIndex':\n    allOf:\n      - $ref: '#/definitions/Data:Bookmark'\n      - properties:\n          index:\n            type: integer\n            example: 42\n            description: \"If omitted (in a list), implies natural order (i.e. enumerated starting from 1)\"\n\n  'Form:Bookmark':\n    allOf:\n      - $ref: '#/definitions/Data:Bookmark'\n      - properties:\n          fetch:\n            type: boolean\n            example: false\n            description: \"'true' enables fetching unspecified data (e.g. title) from the URL\"\n\n  'Form:Tags':\n    type: object\n    properties:\n      tags:\n        type: array\n        items:\n          $ref: '#/definitions/Value:Tag'\n        example: ['bar', 'baz']\n\n  'Response:Success':\n    type: object\n    properties:\n      message:\n        type: string\n        example: \"Success.\"\n      status:\n        type: integer\n        example: 0\n        description: 0 indicates successful completion of the request\n\n  'Response:Failure':\n    type: object\n    properties:\n      message:\n        type: string\n        example: \"Failure.\"\n      status:\n        type: integer\n        example: 1\n        description: 1 indicates failed completion of the request\n\n  'Response:IllFormedRequest':\n    allOf:\n      - $ref: '#/definitions/Response:Failure'\n      - properties:\n          message:\n            example: \"Ill-formed request.\"\n\n  'Response:InputNotValid':\n    allOf:\n      - $ref: '#/definitions/Response:Failure'\n      - properties:\n          message:\n            example: \"Input data not valid.\"\n          errors:\n            type: object\n            additionalProperties: {}\n\n  'Response:InputNotValid:Tags':\n    allOf:\n      - $ref: '#/definitions/Response:InputNotValid'\n      - properties:\n          errors:\n            properties:\n              tags:\n                description: \"A single error for the field itself, or a list containing error lists for each tag.\"\n                example: [\"This field is required.\"]\n\n  'Response:InputNotValid:Url':\n    allOf:\n      - $ref: '#/definitions/Response:InputNotValid'\n      - properties:\n          errors:\n            properties:\n              url:\n                type: array\n                items: {type: string}\n                example: [\"This field is required.\"]\n\n  'Response:InputNotValid:Bookmark':\n    allOf:\n      - $ref: '#/definitions/Response:InputNotValid'\n      - properties:\n          errors:\n            properties:\n              url:\n                type: array\n                items: {type: string}\n              title:\n                type: array\n                items: {type: string}\n              tags:\n                type: array\n                items:\n                  type: array\n                  items: {type: string}\n              description:\n                type: array\n                items: {type: string}\n              fetch:\n                type: array\n                items: {type: string}\n            example: {url: [\"This field is required.\"], tags: [[], [\"The value must be a string.\"], [\"Invalid input.\"]]}\n\n  'Response:InputNotValid:BookmarkRange':\n    allOf:\n      - $ref: '#/definitions/Response:InputNotValid'\n      - properties:\n          errors:\n            description: \"Each key is a bookmark index\"\n            additionalProperties:\n              description: \"A single error indicating that the bookmark is missing, or a form validation result\"\n            example:\n              {1: {url: [\"Field must be at least 1 character long.\"]},\n               3: \"Input required.\",\n               4: {tags: [[], [\"The value must be a string.\"], [\"Invalid input.\"]]}}\n\n  'Response:InputNotValid:BookmarkSearch':\n    allOf:\n      - $ref: '#/definitions/Response:InputNotValid'\n      - properties:\n          errors:\n            properties:\n              keywords:\n                type: array\n                items:\n                  type: array\n                  items: {type: string}\n            example: {keywords: [[], [\"The value must be a string.\"]]}\n\n  'Response:NotFound:Bookmark':\n    allOf:\n      - $ref: '#/definitions/Response:Failure'\n      - properties:\n          message:\n            example: \"Bookmark not found.\"\n\n  'Response:NotFound:BookmarkRange':\n    allOf:\n      - $ref: '#/definitions/Response:Failure'\n      - properties:\n          message:\n            example: \"Range not valid.\"\n\n  'Response:NotFound:Tag':\n    allOf:\n      - $ref: '#/definitions/Response:Failure'\n      - properties:\n          message:\n            example: \"Tag not found.\"\n\n  'Response:NotValid:Tag':\n    allOf:\n      - $ref: '#/definitions/Response:Failure'\n      - properties:\n          message:\n            example: \"Invalid tag.\"\n\n  'Response:Removed':\n    allOf:\n      - $ref: '#/definitions/Response:Failure'\n      - properties:\n          message:\n            example: \"Functionality no longer available.\"\n"
  },
  {
    "path": "bukuserver/apidocs/tiny_url/get.yml",
    "content": "Fetch the shortened URL\n`[NO LONGER AVAILABLE]`\n---\n#GET /api/bookmarks/{index}/tiny\n\ntags: [Util]\n\nparameters:\n  - name: index\n    in: path\n    required: true\n    type: integer\n    minimum: 1\n\nresponses:\n  410:\n    description: Functionality no longer available.\n    schema:\n      $ref: '#/definitions/Response:Removed'\n"
  },
  {
    "path": "bukuserver/bookmarklet.js",
    "content": "// source for the bookmarklet in templates/bukuserver/bookmarklet.url:\n//\n// 1. paste this code in https://bookmarklets.org/maker/\n// 2. replace contents of bookmarklet.url file with the result\n//\n// (\"{{url}}\" will be substituted with actual URL at runtime)\n\nvar url = location.href;\nvar title = document.title.trim() || \"\";\nvar desc = document.getSelection().toString().trim() || (document.querySelector('meta[name$=description i], meta[property$=description i]')||{}).content || \"\";\nif(desc.length > 4000){\n    desc = desc.substr(0,4000) + '...';\n    alert('The selected text is too long, it will be truncated.');\n}\nurl = \"{{url}}\" +\n    \"?url=\" + encodeURIComponent(url) +\n    \"&title=\" + encodeURIComponent(title) +\n    \"&description=\" + encodeURIComponent(desc);\nwindow.open(url, '_blank', 'menubar=no, height=700, width=800, toolbar=no, scrollbars=yes, status=no, dialog=1');\n"
  },
  {
    "path": "bukuserver/filters.py",
    "content": "from enum import Enum\n\nfrom flask_admin.model import filters\nfrom bukuserver import _l, _key\n\n\nclass BookmarkField(Enum):\n    ID = 0\n    URL = 1\n    TITLE = 2\n    TAGS = 3\n    DESCRIPTION = 4\n\n\ndef equal_func(query, value, index):\n    return filter(lambda x: x[index] == value, query)\n\n\ndef not_equal_func(query, value, index):\n    return filter(lambda x: x[index] != value, query)\n\n\ndef contains_func(query, value, index):\n    return filter(lambda x: value in x[index], query)\n\n\ndef not_contains_func(query, value, index):\n    return filter(lambda x: value not in x[index], query)\n\n\ndef greater_func(query, value, index):\n    return filter(lambda x: x[index] > value, query)\n\n\ndef smaller_func(query, value, index):\n    return filter(lambda x: x[index] < value, query)\n\n\ndef in_list_func(query, value, index):\n    return filter(lambda x: x[index] in value, query)\n\n\ndef not_in_list_func(query, value, index):\n    return filter(lambda x: x[index] not in value, query)\n\n\ndef top_x_func(query, value, index):\n    items = sorted(set(x[index] for x in query), reverse=True)\n    top_x = set(items[:value])\n    return filter((lambda x: x[index] in top_x), query)\n\n\ndef bottom_x_func(query, value, index):\n    items = sorted(set(x[index] for x in query), reverse=False)\n    top_x = set(items[:value])\n    return filter((lambda x: x[index] in top_x), query)\n\n\nclass FilterType(Enum):\n\n    EQUAL = {'func': equal_func, 'text': _l('equals')}\n    NOT_EQUAL = {'func': not_equal_func, 'text': _l('not equals')}\n    CONTAINS = {'func': contains_func, 'text': _l('contains')}\n    NOT_CONTAINS = {'func': not_contains_func, 'text': _l('not contains')}\n    GREATER = {'func': greater_func, 'text': _l('greater than')}\n    SMALLER = {'func': smaller_func, 'text': _l('smaller than')}\n    IN_LIST = {'func': in_list_func, 'text': _l('in list')}\n    NOT_IN_LIST = {'func': not_in_list_func, 'text': _l('not in list')}\n    TOP_X = {'func': top_x_func, 'text': _l('top X')}\n    BOTTOM_X = {'func': bottom_x_func, 'text': _l('bottom X')}\n\n\nclass BaseFilter(filters.BaseFilter):\n\n    def operation(self):\n        return getattr(self, 'operation_text')\n\n    def apply(self, query, value):\n        return getattr(self, 'apply_func')(query, value, getattr(self, 'index'))\n\n\nclass TagBaseFilter(BaseFilter):\n\n    def __init__(\n            self,\n            name,\n            operation_text=None,\n            apply_func=None,\n            filter_type=None,\n            options=None,\n            data_type=None):\n        try:\n            self.index = ['name', 'usage_count'].index(name)\n        except ValueError as e:\n            raise ValueError(f'name: {name}') from e\n        self.filter_type = filter_type\n        if filter_type:\n            self.apply_func = filter_type.value['func']\n            self.operation_text = filter_type.value['text']\n        else:\n            self.apply_func = apply_func\n            self.operation_text = operation_text\n        if _key(self.operation_text) in ('in list', 'not in list'):\n            super().__init__(name, options, data_type='select2-tags')\n        else:\n            super().__init__(name, options, data_type)\n\n    def clean(self, value):\n        on_list = self.filter_type in (FilterType.IN_LIST, FilterType.NOT_IN_LIST)\n        if on_list and self.name == 'usage_count':\n            value = [int(v.strip()) for v in value.split(',') if v.strip()]\n        elif on_list:\n            value = [v.strip() for v in value.split(',') if v.strip()]\n        elif self.name == 'usage_count':\n            value = int(value)\n            if self.filter_type in (FilterType.TOP_X, FilterType.BOTTOM_X) and value < 1:\n                raise ValueError\n        if isinstance(value, str):\n            return value.strip()\n        return value\n\n\nclass BookmarkOrderFilter(BaseFilter):\n    DIR_LIST = [('asc', _l('natural')), ('desc', _l('reversed'))]\n    FIELDS = ['index', 'url', 'netloc', 'title', 'description', 'tags', '-#', '#']\n    _NAMES = {'-#': 'with tag first', '#': 'with tag last'}\n\n    def __init__(self, field, *args, **kwargs):\n        self.field = field\n        super().__init__('order', *args, options=(None if field in self._NAMES else self.DIR_LIST), **kwargs)\n\n    def operation(self):\n        key = self._NAMES.get(self.field, f'by {self.field}')\n        return _l(key)\n\n    def apply(self, query, value):\n        return query\n\n    @staticmethod\n    def value(filters, values):\n        return [(filters[idx].field + value if filters[idx].field in BookmarkOrderFilter._NAMES else\n                 ('-' if value == 'desc' else '+') + filters[idx].field)\n                for idx, key, value in values if key == 'order']\n\n\nclass BookmarkBukuFilter(BaseFilter):\n    KEYS = {\n        'markers': 'markers',\n        'all_keywords': 'match all',\n        'deep': 'deep',\n        'regex': 'regex',\n    }\n\n    def __init__(self, *args, **kwargs):\n        self.params = {key: kwargs.pop(key, False) for key in self.KEYS}\n        super().__init__('buku', *args, **kwargs)\n\n    def operation(self):\n        parts = ', '.join(v for k, v in self.KEYS.items() if self.params[k])\n        key = 'search' + (parts and ' ' + parts)\n        return _l(key)\n\n    def apply(self, query, value):\n        return query\n\n\nclass BookmarkBaseFilter(BaseFilter):\n\n    def __init__(\n            self,\n            name,\n            operation_text=None,\n            apply_func=None,\n            filter_type=None,\n            options=None,\n            data_type=None):\n        bm_fields_dict = {x.name.lower(): x.value for x in BookmarkField}\n        if name in bm_fields_dict:\n            self.index = bm_fields_dict[name]\n        else:\n            raise ValueError(f'name: {name}')\n        self.filter_type = None\n        if filter_type:\n            self.apply_func = filter_type.value['func']\n            self.operation_text = filter_type.value['text']\n        else:\n            self.apply_func = apply_func\n            self.operation_text = operation_text\n        if _key(self.operation_text) in ('in list', 'not in list'):\n            super().__init__(name, options, data_type='select2-tags')\n        else:\n            super().__init__(name, options, data_type)\n\n    def clean(self, value):\n        on_list = _key(self.operation_text) in ('in list', 'not in list')\n        if on_list and self.name == BookmarkField.ID.name.lower():\n            value = [int(v.strip()) for v in value.split(',') if v.strip()]\n        elif on_list:\n            value = [v.strip() for v in value.split(',') if v.strip()]\n        elif self.name == BookmarkField.ID.name.lower():\n            value = int(value)\n            if self.filter_type in (FilterType.TOP_X, FilterType.BOTTOM_X) and value < 1:\n                raise ValueError\n        if isinstance(value, str):\n            return value.strip()\n        return value\n\n\nclass BookmarkTagNumberEqualFilter(BookmarkBaseFilter):\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n\n        def apply_func(query, value, index):\n            for item in query:\n                tags = [tag for tag in item[index].split(',') if tag]\n                if len(tags) == value:\n                    yield item\n\n        self.apply_func = apply_func\n\n    def clean(self, value):\n        value = int(value)\n        if value < 0:\n            raise ValueError\n        return value\n\n\nclass BookmarkTagNumberGreaterFilter(BookmarkTagNumberEqualFilter):\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n\n        def apply_func(query, value, index):\n            for item in query:\n                tags = [tag for tag in item[index].split(',') if tag]\n                if len(tags) > value:\n                    yield item\n\n        self.apply_func = apply_func\n\n\nclass BookmarkTagNumberNotEqualFilter(BookmarkTagNumberEqualFilter):\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n\n        def apply_func(query, value, index):\n            for item in query:\n                tags = [tag for tag in item[index].split(',') if tag]\n                if len(tags) != value:\n                    yield item\n\n        self.apply_func = apply_func\n\n\nclass BookmarkTagNumberSmallerFilter(BookmarkBaseFilter):\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n\n        def apply_func(query, value, index):\n            for item in query:\n                tags = [tag for tag in item[index].split(',') if tag]\n                if len(tags) < value:\n                    yield item\n\n        self.apply_func = apply_func\n\n    def clean(self, value):\n        value = int(value)\n        if value < 1:\n            raise ValueError\n        return value\n"
  },
  {
    "path": "bukuserver/forms.py",
    "content": "\"\"\"Forms module.\"\"\"\n# pylint: disable=too-few-public-methods, missing-docstring\nimport re\nfrom flask_wtf import FlaskForm\nfrom wtforms import Form\nfrom wtforms.fields import BooleanField, FieldList, URLField, StringField, TextAreaField, HiddenField, SelectMultipleField\nfrom wtforms.validators import DataRequired, InputRequired, Length, Regexp, StopValidation\nfrom buku import DELIM, taglist_str\nfrom bukuserver import _, _l, LazyString\n\n_parse_bool = lambda x: str(x).lower() == 'true'\n\nTAG_RE = re.compile(r'^[^,]*[^,\\s]+[^,]*$')\n\ndef optional_none(form, field):\n    if field.data is None:\n        raise StopValidation()\n\ndef is_string(form, field):\n    if not isinstance(field.data, str):\n        raise StopValidation(_('The value must be a string.'))\n\nvalidate_tag = [is_string, Regexp(TAG_RE)]\n\n\nclass ValueList(SelectMultipleField):\n    \"\"\"A form field model for simple value lists, capable of processing regular array data.\"\"\"\n\n    def __init__(self, *args, item_validators=[], **kwargs):\n        self.data, self._valid, self._field = None, True, StringField(validators=item_validators)\n        super().__init__(*args, choices=[], validate_choice=False, coerce=(lambda x: x), **kwargs)\n\n    def process_data(self, value):\n        self._valid = isinstance(value, (list, tuple, set, type(None)))  # i.e. for JSON input\n        self.data = None\n        if self._valid:\n            super().process_data(value)\n\n    def pre_validate(self, form):\n        _errors = []\n        _field = self._field.bind(form=form, name=self.name, _meta=self.meta, translations=self._translations)  # pylint: disable=no-member\n        for item in (self.data or []):\n            _field.data = item\n            _field.validate(form)\n            _errors += [_field.errors]\n        if any(x for x in _errors):\n            self.errors += _errors\n        if not self._valid:\n            raise StopValidation(self.gettext('Invalid input.'))\n\n\nclass SearchBookmarksForm(FlaskForm):\n    keywords = FieldList(StringField(_l('Keywords')), min_entries=1)\n    all_keywords = BooleanField(_l('Match all keywords'), default=True, description=_l('Exclude partial matches (with multiple keywords)'))\n    markers = BooleanField(_l('With markers'), default=True, description=LazyString(lambda: '\\n'.join([\n        _('The search string will be split into multiple keywords, each will be applied to a field based on prefix:'),\n        _(\" - keywords starting with '.', '>' or ':' will be searched for in title, description and URL respectively\"),\n        _(\" - '#' will be searched for in tags (comma-separated, partial matches; not affected by Deep Search)\"),\n        _(\" - '#,' is the same but will match FULL tags only\"),\n        _(\" - '*' will be searched for in all fields (this prefix can be omitted in the 1st keyword)\"),\n        _('Keywords need to be separated by placing spaces before the prefix.'),\n    ])))\n    deep = BooleanField(_l('Deep search'), description=_l('When unset, only FULL words will be matched.'))\n    regex = BooleanField(_l('Regex'), description=_l('The keyword(s) are regular expressions (overrides other options).'))\n\n\nclass HomeForm(SearchBookmarksForm):\n    keyword = StringField(_l('Keyword'))\n\n\nclass BookmarkForm(FlaskForm):\n    url = URLField(_l('URL'), name='link', validators=[InputRequired()])\n    title = StringField(_l('Title'))\n    tags = StringField(_l('Tags'))\n    description = TextAreaField(_l('Description'))\n    fetch = HiddenField(filters=[bool])\n\n\nclass SwapForm(FlaskForm):\n    id1 = HiddenField(filters=[int])\n    id2 = HiddenField(filters=[int])\n\n\nclass ApiFetchDataForm(Form):\n    url = StringField(validators=[DataRequired()])\n\n\nclass ApiTagForm(Form):\n    tags = ValueList(validators=[DataRequired()], item_validators=validate_tag)\n\n    @property\n    def tags_str(self):\n        return (None if self.tags.data is None else taglist_str(DELIM.join(self.tags.data)))\n\n\nclass ApiBookmarkCreateForm(ApiTagForm):\n    url = StringField(validators=[DataRequired()])\n    title = StringField()\n    description = StringField()\n    tags = ValueList(item_validators=validate_tag)\n    fetch = BooleanField(filters=[_parse_bool])\n\n    @property\n    def data_values(self):\n        return [self.url.data, self.title.data, self.description.data, self.tags.data]\n\n    @property\n    def has_data(self):\n        return self.fetch.data or any(self.data_values)\n\n\nclass ApiBookmarkEditForm(ApiBookmarkCreateForm):\n    url = StringField(validators=[optional_none, Length(min=1)])\n\n    @property\n    def has_data(self):  # allowing to delete existing values\n        return self.fetch.data or any(x is not None for x in self.data_values)\n\n\nclass ApiBookmarkRangeEditForm(ApiBookmarkEditForm):\n    del_tags = BooleanField(_('Delete tags list from existing tags'), default=False)\n\n    @property\n    def tags_in(self):\n        return (None if not self.tags.data else ('-' if self.del_tags.data else '+') + self.tags_str)\n\n    @property\n    def data_values(self):  # ignoring empty tags list\n        return [self.url.data, self.title.data, self.description.data, self.tags_in]\n\n\nclass ApiBookmarkSearchForm(Form):\n    keywords = ValueList(validators=[DataRequired()], item_validators=[is_string])\n    all_keywords = BooleanField(filters=[_parse_bool])\n    deep = BooleanField(filters=[_parse_bool])\n    regex = BooleanField(filters=[_parse_bool])\n    markers = BooleanField(filters=[_parse_bool])\n    order = ValueList(item_validators=[is_string])\n\nclass ApiBookmarksReorderForm(Form):\n    order = ValueList(validators=[DataRequired()], item_validators=[is_string])\n"
  },
  {
    "path": "bukuserver/middleware/__init__.py",
    "content": "from .flask_reverse_proxy_fix import ReverseProxyPrefixFix\n\n__all__ = ['ReverseProxyPrefixFix']\n"
  },
  {
    "path": "bukuserver/middleware/flask_reverse_proxy_fix.py",
    "content": "# clone of flask-reverse-proxy-fix with github fixes from @rachmadaniHaryono and @Glocktober\n\nfrom flask import Flask as App\n# noinspection PyPackageRequirements\ntry:\n    from werkzeug.contrib.fixers import ProxyFix\nexcept ModuleNotFoundError:\n    from werkzeug.middleware.proxy_fix import ProxyFix\n\n\nclass ReverseProxyPrefixFix:  # pylint: disable=too-few-public-methods\n    \"\"\"\n    Flask middleware to ensure correct URLs are generated by Flask.url_for() where an application is under a reverse\n    proxy. Specifically this middleware corrects URLs where a common prefix needs to be added to all URLs.\n\n    For example: If client requests for an application are reverse proxied such that:\n    `example.com/some-service/v1/foo` becomes `some-service-v1.internal/foo`, where `/foo` is a route within a Flask\n    application `foo()`.\n\n    Without this middleware, a call to `Flask.url_for('.foo')` would give: `/foo`. If returned to the client, as a\n    'self' link for example, this would cause a request to `example.com/foo`, which would be invalid as the\n    `/some-service/v1` prefix is missing.\n\n    With this middleware, a call to `Flask.url_for('.foo')` would give: '/some-service/v1/foo', which will work if used\n    by a client.\n\n    This middleware is compatible with both relative and absolute URLs (i.e. `Flask.url_for('.foo')` and\n    `Flask.url_for('.foo', _external=True)`.\n\n    This middleware incorporates the `werkzeug.contrib.fixers.ProxyFix` middleware [1] and is based on the\n    'Fixing SCRIPT_NAME/url_scheme when behind reverse proxy' Flask snippet [2].\n\n    Note: Ensure the prefix value includes a preceding slash, but not a trailing slash (i.e. use `/foo` not `/foo/`).\n\n    [1] http://werkzeug.pocoo.org/docs/0.14/contrib/fixers/#werkzeug.contrib.fixers.ProxyFix\n    [2] http://flask.pocoo.org/snippets/35/\n    \"\"\"\n    def __init__(self, app: App, **kwargs):\n        \"\"\"\n        :type app: App\n        :param app: Flask application\n        \"\"\"\n        self.app = app.wsgi_app\n        self.prefix = None\n\n        if 'REVERSE_PROXY_PATH' in app.config:\n            self.prefix = app.config['REVERSE_PROXY_PATH']\n\n        self.app = ProxyFix(self.app, **kwargs)\n\n        app.wsgi_app = self\n\n    def __call__(self, environ, start_response):\n        if self.prefix is not None:\n            environ['SCRIPT_NAME'] = self.prefix\n            path_info = environ['PATH_INFO']\n            if path_info.startswith(self.prefix):\n                environ['PATH_INFO'] = path_info[len(self.prefix):]\n\n        return self.app(environ, start_response)\n"
  },
  {
    "path": "bukuserver/requirements.txt",
    "content": "arrow>=1.2.2\nFlask-Admin>=2.0.0\nflask-paginate>=2022.1.8\nFlask-WTF>=1.0.1\nFlask>=2.2.2\nJinja2>=3\nflasgger\n"
  },
  {
    "path": "bukuserver/response.py",
    "content": "from typing import Any, Dict\nfrom enum import Enum\nfrom http import HTTPStatus\nfrom flask import jsonify\n\nOK, FAIL = 0, 1\n\n\nclass Response(Enum):\n    SUCCESS = (HTTPStatus.OK, \"Success.\")                                         # 200\n    FAILURE = (HTTPStatus.CONFLICT, \"Failure.\")                                   # 409\n    REMOVED = (HTTPStatus.GONE, \"Functionality no longer available.\")             # 410\n    INVALID_REQUEST = (HTTPStatus.BAD_REQUEST, \"Ill-formed request.\")             # 400\n    INPUT_NOT_VALID = (HTTPStatus.UNPROCESSABLE_ENTITY, \"Input data not valid.\")  # 422\n    TAG_NOT_VALID = (HTTPStatus.UNPROCESSABLE_ENTITY, \"Invalid tag.\")             # 422\n    BOOKMARK_NOT_FOUND = (HTTPStatus.NOT_FOUND, \"Bookmark not found.\")            # 404\n    RANGE_NOT_VALID = (HTTPStatus.NOT_FOUND, \"Range not valid.\")                  # 404\n    TAG_NOT_FOUND = (HTTPStatus.NOT_FOUND, \"Tag not found.\")                      # 404\n\n    @staticmethod\n    def invalid(errors):\n        return Response.INPUT_NOT_VALID(data={'errors': errors})\n\n    @staticmethod\n    def from_flag(flag: bool, *, data: Dict[str, Any] = None, errors: Dict[str, Any] = None):\n        errors = dict(({'errors': errors} if errors else {}), **(data or {}))\n        return Response.SUCCESS(data=data) if flag else Response.FAILURE(data=errors)\n\n    @property\n    def status_code(self) -> int:\n        return self.value[0].value\n\n    @property\n    def message(self) -> str:\n        return self.value[1]\n\n    @property\n    def status(self) -> int:\n        return OK if self.status_code == HTTPStatus.OK.value else FAIL\n\n    def json(self, data: Dict[str, Any] = None) -> Dict[str, Any]:\n        return dict(status=self.status, message=self.message, **data or {})  # pylint: disable=R1735\n\n    def __call__(self, *, data: Dict[str, Any] = None):\n        \"\"\"Generates a tuple in the form (response, status, headers)\n\n        If passed, data is added to the response's JSON.\n        \"\"\"\n\n        return (jsonify(self.json(data)), self.status_code, {'ContentType': 'application/json'})\n"
  },
  {
    "path": "bukuserver/server.py",
    "content": "#!/usr/bin/env python\n# pylint: disable=wrong-import-order, ungrouped-imports\n\"\"\"Server module.\"\"\"\nimport os\nimport sys\nimport importlib.metadata\nfrom urllib.parse import urlsplit, urlunsplit, parse_qs\n\nimport click\nimport flask\nfrom flask import Flask, redirect, request, url_for\nfrom flask.cli import FlaskGroup\nfrom flask_admin import Admin\nfrom flask_admin.theme import Bootstrap4Theme\nfrom flasgger import Swagger\n\nfrom buku import BukuDb, __version__\n\ntry:\n    from .middleware import ReverseProxyPrefixFix\nexcept ImportError:\n    from bukuserver.middleware import ReverseProxyPrefixFix\n\ntry:\n    from . import api, views, util, _p, _l, gettext, ngettext\nexcept ImportError:\n    from bukuserver import api, views, util, _p, _l, gettext, ngettext\n\nFLASK_VERSION = importlib.metadata.version('flask')\n\n\n_BOOL_VALUES = {'true': True, '1': True, 'false': False, '0': False}\ndef get_bool_from_env_var(key: str, default_value: bool = False) -> bool:\n    \"\"\"Get bool value from env var.\"\"\"\n    return _BOOL_VALUES.get(os.getenv(key, '').lower(), default_value)\n\n\ndef init_locale(app, context_processor=lambda: {}):\n    try:\n        from flask_babel import Babel\n        Babel().init_app(app, locale_selector=lambda: app.config['BUKUSERVER_LOCALE'])\n        app.context_processor(lambda: {'lang': app.config['BUKUSERVER_LOCALE'] or 'en', **context_processor()})\n    except Exception as e:\n        app.jinja_env.add_extension('jinja2.ext.i18n')\n        app.jinja_env.install_gettext_callables(gettext, ngettext, newstyle=True)\n        app.logger.warning(f'failed to init locale ({e})')\n        app.context_processor(lambda: {'lang': '', **context_processor()})\n\n\n# handling popup= URL argument\ndef before_request():\n    _post_popup = request.headers.get('Content-Type') != 'application/json' and request.form.get('popup')\n    flask.g.popup = request.args.get('popup') or _post_popup\n\n# applying popup= to the redirect URL\ndef after_request(response):\n    if flask.g.popup and 'Location' in response.headers:\n        _scheme, _netloc, _path, _query, _fragment = urlsplit(response.headers['Location'])\n        _params = parse_qs(_query)\n        if not _params.get('popup'):\n            _query = '&'.join(s for s in [_query, 'popup=True'] if s)\n            response.headers['Location'] = urlunsplit((_scheme, _netloc, _path, _query, _fragment))\n    return response\n\n\ndef create_app(db_file=None):\n    \"\"\"create app.\"\"\"\n    app = Flask(__name__)\n    db_file = os.getenv('BUKUSERVER_DB_FILE') or db_file\n    if db_file and not os.path.dirname(db_file) and not os.path.splitext(db_file)[1]:\n        db_file = os.path.join(BukuDb.get_default_dbdir(), db_file + '.db')\n    os.environ.setdefault('FLASK_DEBUG', ('1' if get_bool_from_env_var('BUKUSERVER_DEBUG') else '0'))\n    per_page = int(os.getenv('BUKUSERVER_PER_PAGE', str(views.DEFAULT_PER_PAGE)))\n    per_page = per_page if per_page > 0 else views.DEFAULT_PER_PAGE\n    app.config['BUKUSERVER_PER_PAGE'] = per_page\n    url_render_mode = os.getenv('BUKUSERVER_URL_RENDER_MODE', views.DEFAULT_URL_RENDER_MODE)\n    if url_render_mode not in ('full', 'netloc', 'netloc-tag'):\n        url_render_mode = views.DEFAULT_URL_RENDER_MODE\n    app.config['BUKUSERVER_URL_RENDER_MODE'] = url_render_mode\n    app.config['SECRET_KEY'] = os.getenv('BUKUSERVER_SECRET_KEY') or os.urandom(24)\n    app.config['BUKUSERVER_READONLY'] = \\\n        get_bool_from_env_var('BUKUSERVER_READONLY')\n    app.config['BUKUSERVER_DISABLE_FAVICON'] = \\\n        get_bool_from_env_var('BUKUSERVER_DISABLE_FAVICON', True)\n    app.config['BUKUSERVER_OPEN_IN_NEW_TAB'] = \\\n        get_bool_from_env_var('BUKUSERVER_OPEN_IN_NEW_TAB')\n    app.config['BUKUSERVER_AUTOFETCH'] = \\\n        get_bool_from_env_var('BUKUSERVER_AUTOFETCH', True)\n    app.config['BUKUSERVER_DB_FILE'] = db_file\n    # Mitigate Host header poisoning: when set, url_for(..., _external=True) uses\n    # this instead of the client-supplied Host header. Recommended for production\n    # and when behind a reverse proxy (e.g. BUKUSERVER_SERVER_NAME=example.com:443).\n    server_name = os.getenv('BUKUSERVER_SERVER_NAME')\n    if server_name:\n        app.config['SERVER_NAME'] = server_name\n    reverse_proxy_path = os.getenv('BUKUSERVER_REVERSE_PROXY_PATH')\n    if reverse_proxy_path:\n        if not reverse_proxy_path.startswith('/'):\n            print('Warning: reverse proxy path should include preceding slash')\n        if reverse_proxy_path.endswith('/'):\n            print('Warning: reverse proxy path should not include trailing slash')\n        app.config['REVERSE_PROXY_PATH'] = reverse_proxy_path\n        ReverseProxyPrefixFix(app)\n    bukudb = BukuDb(dbfile=db_file)\n    theme = (os.getenv('BUKUSERVER_THEME') or 'default').lower()\n    app.config['BUKUSERVER_LOCALE'] = os.getenv('BUKUSERVER_LOCALE') or 'en'\n    _dir = os.path.dirname(os.path.realpath(__file__))\n    app.config['SWAGGER'] = {'title': 'Bukuserver API', 'doc_dir': os.path.join(_dir, 'apidocs')}\n    app.app_context().push()\n    setattr(flask.g, 'bukudb', bukudb)\n    init_locale(app)\n    app.before_request(before_request)\n    app.after_request(after_request)\n\n    @app.shell_context_processor\n    def shell_context():\n        \"\"\"Shell context definition.\"\"\"\n        return {'app': app, 'bukudb': bukudb}\n\n    app.jinja_env.filters.update(util.JINJA_FILTERS)\n    app.jinja_env.globals.update(_p=_p, dbfile=bukudb.dbfile, dbname=bukudb.dbname)\n\n    admin = Admin(\n        app, name='buku server', theme=Bootstrap4Theme(swatch=theme),\n        index_view=views.CustomAdminIndexView(\n            template='bukuserver/home.html', url='/'\n        )\n    )\n    Swagger(app, template_file=os.path.join(_dir, 'apidocs', 'template.yml'))\n    # routing\n    #  api\n    app.add_url_rule('/api/tags', 'get_all_tags', api.get_all_tags, methods=['GET'], strict_slashes=False)\n    app.add_url_rule('/api/tags/<tag>', view_func=api.ApiTagView.as_view('tag'), methods=['GET', 'PUT', 'DELETE'])\n    app.add_url_rule('/api/bookmarks', view_func=api.ApiBookmarksView.as_view('bookmarks'), methods=['GET', 'POST', 'DELETE'])\n    app.add_url_rule('/api/bookmarks/<int:index>', view_func=api.ApiBookmarkView.as_view('bookmark'), methods=['GET', 'PUT', 'DELETE'])\n    app.add_url_rule('/api/bookmarks/refresh', 'bookmarks_refresh', api.refresh_bookmark, defaults={'index': None}, methods=['POST'])\n    app.add_url_rule('/api/bookmarks/reorder', 'bookmarks_reorder', api.reorder_bookmarks, methods=['POST'])\n    app.add_url_rule('/api/bookmarks/<int:index>/refresh', 'bookmark_refresh', api.refresh_bookmark, methods=['POST'])\n    app.add_url_rule('/api/bookmarks/<int:index>/tiny', 'tiny_url', api.get_tiny_url, methods=['GET'])\n    app.add_url_rule('/api/bookmarks/<int:start_index>/<int:end_index>',\n                     view_func=api.ApiBookmarkRangeView.as_view('bookmark_range'), methods=['GET', 'PUT', 'DELETE'])\n    app.add_url_rule('/api/bookmarks/search', view_func=api.ApiBookmarkSearchView.as_view('bookmarks_search'), methods=['GET', 'DELETE'])\n    app.add_url_rule('/api/network_handle', 'network_handle', api.handle_network, methods=['POST'])\n    app.add_url_rule('/api/fetch_data', 'fetch_data', api.fetch_data, methods=['POST'])\n\n    #  non api\n    @app.route('/favicon.ico')\n    def favicon():\n        return redirect(url_for('static', filename='bukuserver/favicon.svg'), code=301)  # permanent redirect\n\n    app.add_url_rule('/bookmarklet', 'bookmarklet', api.bookmarklet_redirect, methods=['GET'])\n    admin.add_view(views.BookmarkModelView(bukudb, _l('Bookmarks')))\n    admin.add_view(views.TagModelView(bukudb, _l('Tags')))\n    admin.add_view(views.StatisticView(bukudb, _l('Statistic'), endpoint='statistic'))\n    return app\n\n\nclass CustomFlaskGroup(FlaskGroup):  # pylint: disable=too-few-public-methods\n    def __init__(self, **kwargs):\n        super().__init__(**kwargs)\n        for idx, param in enumerate(self.params):\n            if param.name == \"version\":\n                self.params[idx].help = \"Show the program version\"\n                self.params[idx].callback = get_custom_version\n\n\ndef get_custom_version(ctx, param, value):\n    if not value or ctx.resilient_parsing:\n        return\n    message = \"\\n\".join([\"%(app_name)s %(app_version)s\", \"Flask %(version)s\", \"Python %(python_version)s\"])\n    click.echo(\n        message\n        % {\n            \"app_name\": \"buku\",\n            \"app_version\": __version__,\n            \"version\": FLASK_VERSION,\n            \"python_version\": sys.version,\n        },\n        color=ctx.color,\n    )\n    ctx.exit()\n\n\n@click.group(cls=CustomFlaskGroup, create_app=create_app)\ndef cli():\n    \"\"\"This is a script for the bukuserver application.\"\"\"\n\n\nif __name__ == '__main__':\n    cli()\n"
  },
  {
    "path": "bukuserver/static/bukuserver/css/bookmark.css",
    "content": "/* preventing layout overflow in tables */\ntable tr td:not(:first-child) {\n    overflow-wrap: anywhere;\n}\n\n/* wider modals for wide data */\n@media (min-width: 768px) {\n    .modal-dialog {\n        width: min(1000px, 80%);\n    }\n}\n\n/* fixing details filter width */\n#fa_filter {flex-grow: 1}\n\n/* fixing tags input height */\n.select2-container.form-control {display: flex !important}\n.select2-container.form-control .select2-choices {flex-grow: 1}\n\n.link {\n    display: block;\n}\n\n.tag-list {\n    display: flex;\n    gap: 1px;\n    flex-wrap: wrap;\n}\n.tag-list, .link .netloc {\n    font-size: larger;\n}\n.tag-list a, .link .netloc, .select2-search-choice > *, .select2-result-label {\n    white-space: pre;\n}\n\n.description {\n    white-space: pre-wrap;\n}\n"
  },
  {
    "path": "bukuserver/static/bukuserver/css/list.css",
    "content": "/* overriding icon-button text color with theme color */\nform.icon button {\n    color: inherit;\n}\n\n/* fixing table layout */\n.list-buttons-column {width: 0}\n.list-buttons-column .icon:last-child {margin-right: 10px}\n.list-buttons-column .swap-toolbar .icon:last-child {margin-right: 9px}\n\n.filters .filter-op  {width: var(--filter-op) !important}\n.filters .filter-val {width: calc(var(--filters) - var(--filter-op) - var(--filter-buttons) - var(--filter-type)) !important}\n#filter_form[action^='/tag/'] {--filter-type: var(--filter-type-tags)}\n\n:root {--filters: 510px;  --filter-op: 9rem;  --filter-buttons: 12.5rem;  --filter-type: 6rem;  --filter-type-tags: 9.5rem}\n/* due to how flask-admin filters are set up, each language requires manual adjustments for full-width sizes */\nhtml[lang=de] {--filter-buttons: 18rem}\nhtml[lang=fr] {--filter-buttons: 16rem}\nhtml[lang=ru] {--filter-buttons: 16.5rem;  --filter-type: 8.5rem;  --filter-type-tags: 11.5rem}\n\n@media (max-width: 767px) {\n  .filters .filter-val {width: calc(var(--filters) - var(--filter-op) - var(--filter-type)) !important}\n  #filter_form .pull-right:first-child {margin: .5ex 0}\n}\n@media (min-width: 768px) {\n  :root {--filters: 690px;  --filter-op: 11.5rem}\n}\n@media (min-width: 992px) {\n  :root {--filters: 930px;  --filter-op: 20rem}\n}\n@media (min-width: 1200px) {\n  :root {--filters: 1110px;  --filter-op: 20rem}\n  html[lang=ru] #filter_form[action^='/bookmark/'] {--filter-op: 25rem} /* the last 'buku' filter has a rather long name */\n}\n"
  },
  {
    "path": "bukuserver/static/bukuserver/css/modal.css",
    "content": "/* prevent unnecessary scrollbox from appearing at the main window */\nbody.modal-open {\n    overflow-y: inherit;\n}\n\n/* limit dialog height with a scrollbox */\n.modal-content {\n    max-height: calc(100vh - 3.5rem);\n    display: flex;\n    flex-direction: column;\n}\n.modal-body {\n    max-height: 100%;\n    overflow: auto;\n}\n\n/* header size should not be affected */\n.modal-header {\n    flex-shrink: 0;\n}\n\n/* make the table header sticky */\n.modal-body thead {\n    position: sticky;\n    /* setting `top:` with JS, to account for theme differences */\n}\n\n/* overriding icon-button text color with theme color */\n.modal-header .close {\n    color: inherit;\n}\n"
  },
  {
    "path": "bukuserver/static/bukuserver/js/Chart.js",
    "content": "/*!\n * Chart.js\n * http://chartjs.org/\n * Version: 2.7.2\n *\n * Copyright 2018 Chart.js Contributors\n * Released under the MIT license\n * https://github.com/chartjs/Chart.js/blob/master/LICENSE.md\n */\n(function(f){if(typeof exports===\"object\"&&typeof module!==\"undefined\"){module.exports=f()}else if(typeof define===\"function\"&&define.amd){define([],f)}else{var g;if(typeof window!==\"undefined\"){g=window}else if(typeof global!==\"undefined\"){g=global}else if(typeof self!==\"undefined\"){g=self}else{g=this}g.Chart = f()}})(function(){var define,module,exports;return (function(){function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s}return e})()({1:[function(require,module,exports){\n\n},{}],2:[function(require,module,exports){\n/* MIT license */\nvar colorNames = require(6);\n\nmodule.exports = {\n   getRgba: getRgba,\n   getHsla: getHsla,\n   getRgb: getRgb,\n   getHsl: getHsl,\n   getHwb: getHwb,\n   getAlpha: getAlpha,\n\n   hexString: hexString,\n   rgbString: rgbString,\n   rgbaString: rgbaString,\n   percentString: percentString,\n   percentaString: percentaString,\n   hslString: hslString,\n   hslaString: hslaString,\n   hwbString: hwbString,\n   keyword: keyword\n}\n\nfunction getRgba(string) {\n   if (!string) {\n      return;\n   }\n   var abbr =  /^#([a-fA-F0-9]{3})$/i,\n       hex =  /^#([a-fA-F0-9]{6})$/i,\n       rgba = /^rgba?\\(\\s*([+-]?\\d+)\\s*,\\s*([+-]?\\d+)\\s*,\\s*([+-]?\\d+)\\s*(?:,\\s*([+-]?[\\d\\.]+)\\s*)?\\)$/i,\n       per = /^rgba?\\(\\s*([+-]?[\\d\\.]+)\\%\\s*,\\s*([+-]?[\\d\\.]+)\\%\\s*,\\s*([+-]?[\\d\\.]+)\\%\\s*(?:,\\s*([+-]?[\\d\\.]+)\\s*)?\\)$/i,\n       keyword = /(\\w+)/;\n\n   var rgb = [0, 0, 0],\n       a = 1,\n       match = string.match(abbr);\n   if (match) {\n      match = match[1];\n      for (var i = 0; i < rgb.length; i++) {\n         rgb[i] = parseInt(match[i] + match[i], 16);\n      }\n   }\n   else if (match = string.match(hex)) {\n      match = match[1];\n      for (var i = 0; i < rgb.length; i++) {\n         rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16);\n      }\n   }\n   else if (match = string.match(rgba)) {\n      for (var i = 0; i < rgb.length; i++) {\n         rgb[i] = parseInt(match[i + 1]);\n      }\n      a = parseFloat(match[4]);\n   }\n   else if (match = string.match(per)) {\n      for (var i = 0; i < rgb.length; i++) {\n         rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);\n      }\n      a = parseFloat(match[4]);\n   }\n   else if (match = string.match(keyword)) {\n      if (match[1] == \"transparent\") {\n         return [0, 0, 0, 0];\n      }\n      rgb = colorNames[match[1]];\n      if (!rgb) {\n         return;\n      }\n   }\n\n   for (var i = 0; i < rgb.length; i++) {\n      rgb[i] = scale(rgb[i], 0, 255);\n   }\n   if (!a && a != 0) {\n      a = 1;\n   }\n   else {\n      a = scale(a, 0, 1);\n   }\n   rgb[3] = a;\n   return rgb;\n}\n\nfunction getHsla(string) {\n   if (!string) {\n      return;\n   }\n   var hsl = /^hsla?\\(\\s*([+-]?\\d+)(?:deg)?\\s*,\\s*([+-]?[\\d\\.]+)%\\s*,\\s*([+-]?[\\d\\.]+)%\\s*(?:,\\s*([+-]?[\\d\\.]+)\\s*)?\\)/;\n   var match = string.match(hsl);\n   if (match) {\n      var alpha = parseFloat(match[4]);\n      var h = scale(parseInt(match[1]), 0, 360),\n          s = scale(parseFloat(match[2]), 0, 100),\n          l = scale(parseFloat(match[3]), 0, 100),\n          a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);\n      return [h, s, l, a];\n   }\n}\n\nfunction getHwb(string) {\n   if (!string) {\n      return;\n   }\n   var hwb = /^hwb\\(\\s*([+-]?\\d+)(?:deg)?\\s*,\\s*([+-]?[\\d\\.]+)%\\s*,\\s*([+-]?[\\d\\.]+)%\\s*(?:,\\s*([+-]?[\\d\\.]+)\\s*)?\\)/;\n   var match = string.match(hwb);\n   if (match) {\n    var alpha = parseFloat(match[4]);\n      var h = scale(parseInt(match[1]), 0, 360),\n          w = scale(parseFloat(match[2]), 0, 100),\n          b = scale(parseFloat(match[3]), 0, 100),\n          a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);\n      return [h, w, b, a];\n   }\n}\n\nfunction getRgb(string) {\n   var rgba = getRgba(string);\n   return rgba && rgba.slice(0, 3);\n}\n\nfunction getHsl(string) {\n  var hsla = getHsla(string);\n  return hsla && hsla.slice(0, 3);\n}\n\nfunction getAlpha(string) {\n   var vals = getRgba(string);\n   if (vals) {\n      return vals[3];\n   }\n   else if (vals = getHsla(string)) {\n      return vals[3];\n   }\n   else if (vals = getHwb(string)) {\n      return vals[3];\n   }\n}\n\n// generators\nfunction hexString(rgb) {\n   return \"#\" + hexDouble(rgb[0]) + hexDouble(rgb[1])\n              + hexDouble(rgb[2]);\n}\n\nfunction rgbString(rgba, alpha) {\n   if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {\n      return rgbaString(rgba, alpha);\n   }\n   return \"rgb(\" + rgba[0] + \", \" + rgba[1] + \", \" + rgba[2] + \")\";\n}\n\nfunction rgbaString(rgba, alpha) {\n   if (alpha === undefined) {\n      alpha = (rgba[3] !== undefined ? rgba[3] : 1);\n   }\n   return \"rgba(\" + rgba[0] + \", \" + rgba[1] + \", \" + rgba[2]\n           + \", \" + alpha + \")\";\n}\n\nfunction percentString(rgba, alpha) {\n   if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {\n      return percentaString(rgba, alpha);\n   }\n   var r = Math.round(rgba[0]/255 * 100),\n       g = Math.round(rgba[1]/255 * 100),\n       b = Math.round(rgba[2]/255 * 100);\n\n   return \"rgb(\" + r + \"%, \" + g + \"%, \" + b + \"%)\";\n}\n\nfunction percentaString(rgba, alpha) {\n   var r = Math.round(rgba[0]/255 * 100),\n       g = Math.round(rgba[1]/255 * 100),\n       b = Math.round(rgba[2]/255 * 100);\n   return \"rgba(\" + r + \"%, \" + g + \"%, \" + b + \"%, \" + (alpha || rgba[3] || 1) + \")\";\n}\n\nfunction hslString(hsla, alpha) {\n   if (alpha < 1 || (hsla[3] && hsla[3] < 1)) {\n      return hslaString(hsla, alpha);\n   }\n   return \"hsl(\" + hsla[0] + \", \" + hsla[1] + \"%, \" + hsla[2] + \"%)\";\n}\n\nfunction hslaString(hsla, alpha) {\n   if (alpha === undefined) {\n      alpha = (hsla[3] !== undefined ? hsla[3] : 1);\n   }\n   return \"hsla(\" + hsla[0] + \", \" + hsla[1] + \"%, \" + hsla[2] + \"%, \"\n           + alpha + \")\";\n}\n\n// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax\n// (hwb have alpha optional & 1 is default value)\nfunction hwbString(hwb, alpha) {\n   if (alpha === undefined) {\n      alpha = (hwb[3] !== undefined ? hwb[3] : 1);\n   }\n   return \"hwb(\" + hwb[0] + \", \" + hwb[1] + \"%, \" + hwb[2] + \"%\"\n           + (alpha !== undefined && alpha !== 1 ? \", \" + alpha : \"\") + \")\";\n}\n\nfunction keyword(rgb) {\n  return reverseNames[rgb.slice(0, 3)];\n}\n\n// helpers\nfunction scale(num, min, max) {\n   return Math.min(Math.max(min, num), max);\n}\n\nfunction hexDouble(num) {\n  var str = num.toString(16).toUpperCase();\n  return (str.length < 2) ? \"0\" + str : str;\n}\n\n\n//create a list of reverse color names\nvar reverseNames = {};\nfor (var name in colorNames) {\n   reverseNames[colorNames[name]] = name;\n}\n\n},{\"6\":6}],3:[function(require,module,exports){\n/* MIT license */\nvar convert = require(5);\nvar string = require(2);\n\nvar Color = function (obj) {\n\tif (obj instanceof Color) {\n\t\treturn obj;\n\t}\n\tif (!(this instanceof Color)) {\n\t\treturn new Color(obj);\n\t}\n\n\tthis.valid = false;\n\tthis.values = {\n\t\trgb: [0, 0, 0],\n\t\thsl: [0, 0, 0],\n\t\thsv: [0, 0, 0],\n\t\thwb: [0, 0, 0],\n\t\tcmyk: [0, 0, 0, 0],\n\t\talpha: 1\n\t};\n\n\t// parse Color() argument\n\tvar vals;\n\tif (typeof obj === 'string') {\n\t\tvals = string.getRgba(obj);\n\t\tif (vals) {\n\t\t\tthis.setValues('rgb', vals);\n\t\t} else if (vals = string.getHsla(obj)) {\n\t\t\tthis.setValues('hsl', vals);\n\t\t} else if (vals = string.getHwb(obj)) {\n\t\t\tthis.setValues('hwb', vals);\n\t\t}\n\t} else if (typeof obj === 'object') {\n\t\tvals = obj;\n\t\tif (vals.r !== undefined || vals.red !== undefined) {\n\t\t\tthis.setValues('rgb', vals);\n\t\t} else if (vals.l !== undefined || vals.lightness !== undefined) {\n\t\t\tthis.setValues('hsl', vals);\n\t\t} else if (vals.v !== undefined || vals.value !== undefined) {\n\t\t\tthis.setValues('hsv', vals);\n\t\t} else if (vals.w !== undefined || vals.whiteness !== undefined) {\n\t\t\tthis.setValues('hwb', vals);\n\t\t} else if (vals.c !== undefined || vals.cyan !== undefined) {\n\t\t\tthis.setValues('cmyk', vals);\n\t\t}\n\t}\n};\n\nColor.prototype = {\n\tisValid: function () {\n\t\treturn this.valid;\n\t},\n\trgb: function () {\n\t\treturn this.setSpace('rgb', arguments);\n\t},\n\thsl: function () {\n\t\treturn this.setSpace('hsl', arguments);\n\t},\n\thsv: function () {\n\t\treturn this.setSpace('hsv', arguments);\n\t},\n\thwb: function () {\n\t\treturn this.setSpace('hwb', arguments);\n\t},\n\tcmyk: function () {\n\t\treturn this.setSpace('cmyk', arguments);\n\t},\n\n\trgbArray: function () {\n\t\treturn this.values.rgb;\n\t},\n\thslArray: function () {\n\t\treturn this.values.hsl;\n\t},\n\thsvArray: function () {\n\t\treturn this.values.hsv;\n\t},\n\thwbArray: function () {\n\t\tvar values = this.values;\n\t\tif (values.alpha !== 1) {\n\t\t\treturn values.hwb.concat([values.alpha]);\n\t\t}\n\t\treturn values.hwb;\n\t},\n\tcmykArray: function () {\n\t\treturn this.values.cmyk;\n\t},\n\trgbaArray: function () {\n\t\tvar values = this.values;\n\t\treturn values.rgb.concat([values.alpha]);\n\t},\n\thslaArray: function () {\n\t\tvar values = this.values;\n\t\treturn values.hsl.concat([values.alpha]);\n\t},\n\talpha: function (val) {\n\t\tif (val === undefined) {\n\t\t\treturn this.values.alpha;\n\t\t}\n\t\tthis.setValues('alpha', val);\n\t\treturn this;\n\t},\n\n\tred: function (val) {\n\t\treturn this.setChannel('rgb', 0, val);\n\t},\n\tgreen: function (val) {\n\t\treturn this.setChannel('rgb', 1, val);\n\t},\n\tblue: function (val) {\n\t\treturn this.setChannel('rgb', 2, val);\n\t},\n\thue: function (val) {\n\t\tif (val) {\n\t\t\tval %= 360;\n\t\t\tval = val < 0 ? 360 + val : val;\n\t\t}\n\t\treturn this.setChannel('hsl', 0, val);\n\t},\n\tsaturation: function (val) {\n\t\treturn this.setChannel('hsl', 1, val);\n\t},\n\tlightness: function (val) {\n\t\treturn this.setChannel('hsl', 2, val);\n\t},\n\tsaturationv: function (val) {\n\t\treturn this.setChannel('hsv', 1, val);\n\t},\n\twhiteness: function (val) {\n\t\treturn this.setChannel('hwb', 1, val);\n\t},\n\tblackness: function (val) {\n\t\treturn this.setChannel('hwb', 2, val);\n\t},\n\tvalue: function (val) {\n\t\treturn this.setChannel('hsv', 2, val);\n\t},\n\tcyan: function (val) {\n\t\treturn this.setChannel('cmyk', 0, val);\n\t},\n\tmagenta: function (val) {\n\t\treturn this.setChannel('cmyk', 1, val);\n\t},\n\tyellow: function (val) {\n\t\treturn this.setChannel('cmyk', 2, val);\n\t},\n\tblack: function (val) {\n\t\treturn this.setChannel('cmyk', 3, val);\n\t},\n\n\thexString: function () {\n\t\treturn string.hexString(this.values.rgb);\n\t},\n\trgbString: function () {\n\t\treturn string.rgbString(this.values.rgb, this.values.alpha);\n\t},\n\trgbaString: function () {\n\t\treturn string.rgbaString(this.values.rgb, this.values.alpha);\n\t},\n\tpercentString: function () {\n\t\treturn string.percentString(this.values.rgb, this.values.alpha);\n\t},\n\thslString: function () {\n\t\treturn string.hslString(this.values.hsl, this.values.alpha);\n\t},\n\thslaString: function () {\n\t\treturn string.hslaString(this.values.hsl, this.values.alpha);\n\t},\n\thwbString: function () {\n\t\treturn string.hwbString(this.values.hwb, this.values.alpha);\n\t},\n\tkeyword: function () {\n\t\treturn string.keyword(this.values.rgb, this.values.alpha);\n\t},\n\n\trgbNumber: function () {\n\t\tvar rgb = this.values.rgb;\n\t\treturn (rgb[0] << 16) | (rgb[1] << 8) | rgb[2];\n\t},\n\n\tluminosity: function () {\n\t\t// http://www.w3.org/TR/WCAG20/#relativeluminancedef\n\t\tvar rgb = this.values.rgb;\n\t\tvar lum = [];\n\t\tfor (var i = 0; i < rgb.length; i++) {\n\t\t\tvar chan = rgb[i] / 255;\n\t\t\tlum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4);\n\t\t}\n\t\treturn 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];\n\t},\n\n\tcontrast: function (color2) {\n\t\t// http://www.w3.org/TR/WCAG20/#contrast-ratiodef\n\t\tvar lum1 = this.luminosity();\n\t\tvar lum2 = color2.luminosity();\n\t\tif (lum1 > lum2) {\n\t\t\treturn (lum1 + 0.05) / (lum2 + 0.05);\n\t\t}\n\t\treturn (lum2 + 0.05) / (lum1 + 0.05);\n\t},\n\n\tlevel: function (color2) {\n\t\tvar contrastRatio = this.contrast(color2);\n\t\tif (contrastRatio >= 7.1) {\n\t\t\treturn 'AAA';\n\t\t}\n\n\t\treturn (contrastRatio >= 4.5) ? 'AA' : '';\n\t},\n\n\tdark: function () {\n\t\t// YIQ equation from http://24ways.org/2010/calculating-color-contrast\n\t\tvar rgb = this.values.rgb;\n\t\tvar yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;\n\t\treturn yiq < 128;\n\t},\n\n\tlight: function () {\n\t\treturn !this.dark();\n\t},\n\n\tnegate: function () {\n\t\tvar rgb = [];\n\t\tfor (var i = 0; i < 3; i++) {\n\t\t\trgb[i] = 255 - this.values.rgb[i];\n\t\t}\n\t\tthis.setValues('rgb', rgb);\n\t\treturn this;\n\t},\n\n\tlighten: function (ratio) {\n\t\tvar hsl = this.values.hsl;\n\t\thsl[2] += hsl[2] * ratio;\n\t\tthis.setValues('hsl', hsl);\n\t\treturn this;\n\t},\n\n\tdarken: function (ratio) {\n\t\tvar hsl = this.values.hsl;\n\t\thsl[2] -= hsl[2] * ratio;\n\t\tthis.setValues('hsl', hsl);\n\t\treturn this;\n\t},\n\n\tsaturate: function (ratio) {\n\t\tvar hsl = this.values.hsl;\n\t\thsl[1] += hsl[1] * ratio;\n\t\tthis.setValues('hsl', hsl);\n\t\treturn this;\n\t},\n\n\tdesaturate: function (ratio) {\n\t\tvar hsl = this.values.hsl;\n\t\thsl[1] -= hsl[1] * ratio;\n\t\tthis.setValues('hsl', hsl);\n\t\treturn this;\n\t},\n\n\twhiten: function (ratio) {\n\t\tvar hwb = this.values.hwb;\n\t\thwb[1] += hwb[1] * ratio;\n\t\tthis.setValues('hwb', hwb);\n\t\treturn this;\n\t},\n\n\tblacken: function (ratio) {\n\t\tvar hwb = this.values.hwb;\n\t\thwb[2] += hwb[2] * ratio;\n\t\tthis.setValues('hwb', hwb);\n\t\treturn this;\n\t},\n\n\tgreyscale: function () {\n\t\tvar rgb = this.values.rgb;\n\t\t// http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale\n\t\tvar val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;\n\t\tthis.setValues('rgb', [val, val, val]);\n\t\treturn this;\n\t},\n\n\tclearer: function (ratio) {\n\t\tvar alpha = this.values.alpha;\n\t\tthis.setValues('alpha', alpha - (alpha * ratio));\n\t\treturn this;\n\t},\n\n\topaquer: function (ratio) {\n\t\tvar alpha = this.values.alpha;\n\t\tthis.setValues('alpha', alpha + (alpha * ratio));\n\t\treturn this;\n\t},\n\n\trotate: function (degrees) {\n\t\tvar hsl = this.values.hsl;\n\t\tvar hue = (hsl[0] + degrees) % 360;\n\t\thsl[0] = hue < 0 ? 360 + hue : hue;\n\t\tthis.setValues('hsl', hsl);\n\t\treturn this;\n\t},\n\n\t/**\n\t * Ported from sass implementation in C\n\t * https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209\n\t */\n\tmix: function (mixinColor, weight) {\n\t\tvar color1 = this;\n\t\tvar color2 = mixinColor;\n\t\tvar p = weight === undefined ? 0.5 : weight;\n\n\t\tvar w = 2 * p - 1;\n\t\tvar a = color1.alpha() - color2.alpha();\n\n\t\tvar w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;\n\t\tvar w2 = 1 - w1;\n\n\t\treturn this\n\t\t\t.rgb(\n\t\t\t\tw1 * color1.red() + w2 * color2.red(),\n\t\t\t\tw1 * color1.green() + w2 * color2.green(),\n\t\t\t\tw1 * color1.blue() + w2 * color2.blue()\n\t\t\t)\n\t\t\t.alpha(color1.alpha() * p + color2.alpha() * (1 - p));\n\t},\n\n\ttoJSON: function () {\n\t\treturn this.rgb();\n\t},\n\n\tclone: function () {\n\t\t// NOTE(SB): using node-clone creates a dependency to Buffer when using browserify,\n\t\t// making the final build way to big to embed in Chart.js. So let's do it manually,\n\t\t// assuming that values to clone are 1 dimension arrays containing only numbers,\n\t\t// except 'alpha' which is a number.\n\t\tvar result = new Color();\n\t\tvar source = this.values;\n\t\tvar target = result.values;\n\t\tvar value, type;\n\n\t\tfor (var prop in source) {\n\t\t\tif (source.hasOwnProperty(prop)) {\n\t\t\t\tvalue = source[prop];\n\t\t\t\ttype = ({}).toString.call(value);\n\t\t\t\tif (type === '[object Array]') {\n\t\t\t\t\ttarget[prop] = value.slice(0);\n\t\t\t\t} else if (type === '[object Number]') {\n\t\t\t\t\ttarget[prop] = value;\n\t\t\t\t} else {\n\t\t\t\t\tconsole.error('unexpected color value:', value);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n};\n\nColor.prototype.spaces = {\n\trgb: ['red', 'green', 'blue'],\n\thsl: ['hue', 'saturation', 'lightness'],\n\thsv: ['hue', 'saturation', 'value'],\n\thwb: ['hue', 'whiteness', 'blackness'],\n\tcmyk: ['cyan', 'magenta', 'yellow', 'black']\n};\n\nColor.prototype.maxes = {\n\trgb: [255, 255, 255],\n\thsl: [360, 100, 100],\n\thsv: [360, 100, 100],\n\thwb: [360, 100, 100],\n\tcmyk: [100, 100, 100, 100]\n};\n\nColor.prototype.getValues = function (space) {\n\tvar values = this.values;\n\tvar vals = {};\n\n\tfor (var i = 0; i < space.length; i++) {\n\t\tvals[space.charAt(i)] = values[space][i];\n\t}\n\n\tif (values.alpha !== 1) {\n\t\tvals.a = values.alpha;\n\t}\n\n\t// {r: 255, g: 255, b: 255, a: 0.4}\n\treturn vals;\n};\n\nColor.prototype.setValues = function (space, vals) {\n\tvar values = this.values;\n\tvar spaces = this.spaces;\n\tvar maxes = this.maxes;\n\tvar alpha = 1;\n\tvar i;\n\n\tthis.valid = true;\n\n\tif (space === 'alpha') {\n\t\talpha = vals;\n\t} else if (vals.length) {\n\t\t// [10, 10, 10]\n\t\tvalues[space] = vals.slice(0, space.length);\n\t\talpha = vals[space.length];\n\t} else if (vals[space.charAt(0)] !== undefined) {\n\t\t// {r: 10, g: 10, b: 10}\n\t\tfor (i = 0; i < space.length; i++) {\n\t\t\tvalues[space][i] = vals[space.charAt(i)];\n\t\t}\n\n\t\talpha = vals.a;\n\t} else if (vals[spaces[space][0]] !== undefined) {\n\t\t// {red: 10, green: 10, blue: 10}\n\t\tvar chans = spaces[space];\n\n\t\tfor (i = 0; i < space.length; i++) {\n\t\t\tvalues[space][i] = vals[chans[i]];\n\t\t}\n\n\t\talpha = vals.alpha;\n\t}\n\n\tvalues.alpha = Math.max(0, Math.min(1, (alpha === undefined ? values.alpha : alpha)));\n\n\tif (space === 'alpha') {\n\t\treturn false;\n\t}\n\n\tvar capped;\n\n\t// cap values of the space prior converting all values\n\tfor (i = 0; i < space.length; i++) {\n\t\tcapped = Math.max(0, Math.min(maxes[space][i], values[space][i]));\n\t\tvalues[space][i] = Math.round(capped);\n\t}\n\n\t// convert to all the other color spaces\n\tfor (var sname in spaces) {\n\t\tif (sname !== space) {\n\t\t\tvalues[sname] = convert[space][sname](values[space]);\n\t\t}\n\t}\n\n\treturn true;\n};\n\nColor.prototype.setSpace = function (space, args) {\n\tvar vals = args[0];\n\n\tif (vals === undefined) {\n\t\t// color.rgb()\n\t\treturn this.getValues(space);\n\t}\n\n\t// color.rgb(10, 10, 10)\n\tif (typeof vals === 'number') {\n\t\tvals = Array.prototype.slice.call(args);\n\t}\n\n\tthis.setValues(space, vals);\n\treturn this;\n};\n\nColor.prototype.setChannel = function (space, index, val) {\n\tvar svalues = this.values[space];\n\tif (val === undefined) {\n\t\t// color.red()\n\t\treturn svalues[index];\n\t} else if (val === svalues[index]) {\n\t\t// color.red(color.red())\n\t\treturn this;\n\t}\n\n\t// color.red(100)\n\tsvalues[index] = val;\n\tthis.setValues(space, svalues);\n\n\treturn this;\n};\n\nif (typeof window !== 'undefined') {\n\twindow.Color = Color;\n}\n\nmodule.exports = Color;\n\n},{\"2\":2,\"5\":5}],4:[function(require,module,exports){\n/* MIT license */\n\nmodule.exports = {\n  rgb2hsl: rgb2hsl,\n  rgb2hsv: rgb2hsv,\n  rgb2hwb: rgb2hwb,\n  rgb2cmyk: rgb2cmyk,\n  rgb2keyword: rgb2keyword,\n  rgb2xyz: rgb2xyz,\n  rgb2lab: rgb2lab,\n  rgb2lch: rgb2lch,\n\n  hsl2rgb: hsl2rgb,\n  hsl2hsv: hsl2hsv,\n  hsl2hwb: hsl2hwb,\n  hsl2cmyk: hsl2cmyk,\n  hsl2keyword: hsl2keyword,\n\n  hsv2rgb: hsv2rgb,\n  hsv2hsl: hsv2hsl,\n  hsv2hwb: hsv2hwb,\n  hsv2cmyk: hsv2cmyk,\n  hsv2keyword: hsv2keyword,\n\n  hwb2rgb: hwb2rgb,\n  hwb2hsl: hwb2hsl,\n  hwb2hsv: hwb2hsv,\n  hwb2cmyk: hwb2cmyk,\n  hwb2keyword: hwb2keyword,\n\n  cmyk2rgb: cmyk2rgb,\n  cmyk2hsl: cmyk2hsl,\n  cmyk2hsv: cmyk2hsv,\n  cmyk2hwb: cmyk2hwb,\n  cmyk2keyword: cmyk2keyword,\n\n  keyword2rgb: keyword2rgb,\n  keyword2hsl: keyword2hsl,\n  keyword2hsv: keyword2hsv,\n  keyword2hwb: keyword2hwb,\n  keyword2cmyk: keyword2cmyk,\n  keyword2lab: keyword2lab,\n  keyword2xyz: keyword2xyz,\n\n  xyz2rgb: xyz2rgb,\n  xyz2lab: xyz2lab,\n  xyz2lch: xyz2lch,\n\n  lab2xyz: lab2xyz,\n  lab2rgb: lab2rgb,\n  lab2lch: lab2lch,\n\n  lch2lab: lch2lab,\n  lch2xyz: lch2xyz,\n  lch2rgb: lch2rgb\n}\n\n\nfunction rgb2hsl(rgb) {\n  var r = rgb[0]/255,\n      g = rgb[1]/255,\n      b = rgb[2]/255,\n      min = Math.min(r, g, b),\n      max = Math.max(r, g, b),\n      delta = max - min,\n      h, s, l;\n\n  if (max == min)\n    h = 0;\n  else if (r == max)\n    h = (g - b) / delta;\n  else if (g == max)\n    h = 2 + (b - r) / delta;\n  else if (b == max)\n    h = 4 + (r - g)/ delta;\n\n  h = Math.min(h * 60, 360);\n\n  if (h < 0)\n    h += 360;\n\n  l = (min + max) / 2;\n\n  if (max == min)\n    s = 0;\n  else if (l <= 0.5)\n    s = delta / (max + min);\n  else\n    s = delta / (2 - max - min);\n\n  return [h, s * 100, l * 100];\n}\n\nfunction rgb2hsv(rgb) {\n  var r = rgb[0],\n      g = rgb[1],\n      b = rgb[2],\n      min = Math.min(r, g, b),\n      max = Math.max(r, g, b),\n      delta = max - min,\n      h, s, v;\n\n  if (max == 0)\n    s = 0;\n  else\n    s = (delta/max * 1000)/10;\n\n  if (max == min)\n    h = 0;\n  else if (r == max)\n    h = (g - b) / delta;\n  else if (g == max)\n    h = 2 + (b - r) / delta;\n  else if (b == max)\n    h = 4 + (r - g) / delta;\n\n  h = Math.min(h * 60, 360);\n\n  if (h < 0)\n    h += 360;\n\n  v = ((max / 255) * 1000) / 10;\n\n  return [h, s, v];\n}\n\nfunction rgb2hwb(rgb) {\n  var r = rgb[0],\n      g = rgb[1],\n      b = rgb[2],\n      h = rgb2hsl(rgb)[0],\n      w = 1/255 * Math.min(r, Math.min(g, b)),\n      b = 1 - 1/255 * Math.max(r, Math.max(g, b));\n\n  return [h, w * 100, b * 100];\n}\n\nfunction rgb2cmyk(rgb) {\n  var r = rgb[0] / 255,\n      g = rgb[1] / 255,\n      b = rgb[2] / 255,\n      c, m, y, k;\n\n  k = Math.min(1 - r, 1 - g, 1 - b);\n  c = (1 - r - k) / (1 - k) || 0;\n  m = (1 - g - k) / (1 - k) || 0;\n  y = (1 - b - k) / (1 - k) || 0;\n  return [c * 100, m * 100, y * 100, k * 100];\n}\n\nfunction rgb2keyword(rgb) {\n  return reverseKeywords[JSON.stringify(rgb)];\n}\n\nfunction rgb2xyz(rgb) {\n  var r = rgb[0] / 255,\n      g = rgb[1] / 255,\n      b = rgb[2] / 255;\n\n  // assume sRGB\n  r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);\n  g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);\n  b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);\n\n  var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);\n  var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);\n  var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);\n\n  return [x * 100, y *100, z * 100];\n}\n\nfunction rgb2lab(rgb) {\n  var xyz = rgb2xyz(rgb),\n        x = xyz[0],\n        y = xyz[1],\n        z = xyz[2],\n        l, a, b;\n\n  x /= 95.047;\n  y /= 100;\n  z /= 108.883;\n\n  x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);\n  y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);\n  z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);\n\n  l = (116 * y) - 16;\n  a = 500 * (x - y);\n  b = 200 * (y - z);\n\n  return [l, a, b];\n}\n\nfunction rgb2lch(args) {\n  return lab2lch(rgb2lab(args));\n}\n\nfunction hsl2rgb(hsl) {\n  var h = hsl[0] / 360,\n      s = hsl[1] / 100,\n      l = hsl[2] / 100,\n      t1, t2, t3, rgb, val;\n\n  if (s == 0) {\n    val = l * 255;\n    return [val, val, val];\n  }\n\n  if (l < 0.5)\n    t2 = l * (1 + s);\n  else\n    t2 = l + s - l * s;\n  t1 = 2 * l - t2;\n\n  rgb = [0, 0, 0];\n  for (var i = 0; i < 3; i++) {\n    t3 = h + 1 / 3 * - (i - 1);\n    t3 < 0 && t3++;\n    t3 > 1 && t3--;\n\n    if (6 * t3 < 1)\n      val = t1 + (t2 - t1) * 6 * t3;\n    else if (2 * t3 < 1)\n      val = t2;\n    else if (3 * t3 < 2)\n      val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;\n    else\n      val = t1;\n\n    rgb[i] = val * 255;\n  }\n\n  return rgb;\n}\n\nfunction hsl2hsv(hsl) {\n  var h = hsl[0],\n      s = hsl[1] / 100,\n      l = hsl[2] / 100,\n      sv, v;\n\n  if(l === 0) {\n      // no need to do calc on black\n      // also avoids divide by 0 error\n      return [0, 0, 0];\n  }\n\n  l *= 2;\n  s *= (l <= 1) ? l : 2 - l;\n  v = (l + s) / 2;\n  sv = (2 * s) / (l + s);\n  return [h, sv * 100, v * 100];\n}\n\nfunction hsl2hwb(args) {\n  return rgb2hwb(hsl2rgb(args));\n}\n\nfunction hsl2cmyk(args) {\n  return rgb2cmyk(hsl2rgb(args));\n}\n\nfunction hsl2keyword(args) {\n  return rgb2keyword(hsl2rgb(args));\n}\n\n\nfunction hsv2rgb(hsv) {\n  var h = hsv[0] / 60,\n      s = hsv[1] / 100,\n      v = hsv[2] / 100,\n      hi = Math.floor(h) % 6;\n\n  var f = h - Math.floor(h),\n      p = 255 * v * (1 - s),\n      q = 255 * v * (1 - (s * f)),\n      t = 255 * v * (1 - (s * (1 - f))),\n      v = 255 * v;\n\n  switch(hi) {\n    case 0:\n      return [v, t, p];\n    case 1:\n      return [q, v, p];\n    case 2:\n      return [p, v, t];\n    case 3:\n      return [p, q, v];\n    case 4:\n      return [t, p, v];\n    case 5:\n      return [v, p, q];\n  }\n}\n\nfunction hsv2hsl(hsv) {\n  var h = hsv[0],\n      s = hsv[1] / 100,\n      v = hsv[2] / 100,\n      sl, l;\n\n  l = (2 - s) * v;\n  sl = s * v;\n  sl /= (l <= 1) ? l : 2 - l;\n  sl = sl || 0;\n  l /= 2;\n  return [h, sl * 100, l * 100];\n}\n\nfunction hsv2hwb(args) {\n  return rgb2hwb(hsv2rgb(args))\n}\n\nfunction hsv2cmyk(args) {\n  return rgb2cmyk(hsv2rgb(args));\n}\n\nfunction hsv2keyword(args) {\n  return rgb2keyword(hsv2rgb(args));\n}\n\n// http://dev.w3.org/csswg/css-color/#hwb-to-rgb\nfunction hwb2rgb(hwb) {\n  var h = hwb[0] / 360,\n      wh = hwb[1] / 100,\n      bl = hwb[2] / 100,\n      ratio = wh + bl,\n      i, v, f, n;\n\n  // wh + bl cant be > 1\n  if (ratio > 1) {\n    wh /= ratio;\n    bl /= ratio;\n  }\n\n  i = Math.floor(6 * h);\n  v = 1 - bl;\n  f = 6 * h - i;\n  if ((i & 0x01) != 0) {\n    f = 1 - f;\n  }\n  n = wh + f * (v - wh);  // linear interpolation\n\n  switch (i) {\n    default:\n    case 6:\n    case 0: r = v; g = n; b = wh; break;\n    case 1: r = n; g = v; b = wh; break;\n    case 2: r = wh; g = v; b = n; break;\n    case 3: r = wh; g = n; b = v; break;\n    case 4: r = n; g = wh; b = v; break;\n    case 5: r = v; g = wh; b = n; break;\n  }\n\n  return [r * 255, g * 255, b * 255];\n}\n\nfunction hwb2hsl(args) {\n  return rgb2hsl(hwb2rgb(args));\n}\n\nfunction hwb2hsv(args) {\n  return rgb2hsv(hwb2rgb(args));\n}\n\nfunction hwb2cmyk(args) {\n  return rgb2cmyk(hwb2rgb(args));\n}\n\nfunction hwb2keyword(args) {\n  return rgb2keyword(hwb2rgb(args));\n}\n\nfunction cmyk2rgb(cmyk) {\n  var c = cmyk[0] / 100,\n      m = cmyk[1] / 100,\n      y = cmyk[2] / 100,\n      k = cmyk[3] / 100,\n      r, g, b;\n\n  r = 1 - Math.min(1, c * (1 - k) + k);\n  g = 1 - Math.min(1, m * (1 - k) + k);\n  b = 1 - Math.min(1, y * (1 - k) + k);\n  return [r * 255, g * 255, b * 255];\n}\n\nfunction cmyk2hsl(args) {\n  return rgb2hsl(cmyk2rgb(args));\n}\n\nfunction cmyk2hsv(args) {\n  return rgb2hsv(cmyk2rgb(args));\n}\n\nfunction cmyk2hwb(args) {\n  return rgb2hwb(cmyk2rgb(args));\n}\n\nfunction cmyk2keyword(args) {\n  return rgb2keyword(cmyk2rgb(args));\n}\n\n\nfunction xyz2rgb(xyz) {\n  var x = xyz[0] / 100,\n      y = xyz[1] / 100,\n      z = xyz[2] / 100,\n      r, g, b;\n\n  r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);\n  g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);\n  b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);\n\n  // assume sRGB\n  r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)\n    : r = (r * 12.92);\n\n  g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)\n    : g = (g * 12.92);\n\n  b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)\n    : b = (b * 12.92);\n\n  r = Math.min(Math.max(0, r), 1);\n  g = Math.min(Math.max(0, g), 1);\n  b = Math.min(Math.max(0, b), 1);\n\n  return [r * 255, g * 255, b * 255];\n}\n\nfunction xyz2lab(xyz) {\n  var x = xyz[0],\n      y = xyz[1],\n      z = xyz[2],\n      l, a, b;\n\n  x /= 95.047;\n  y /= 100;\n  z /= 108.883;\n\n  x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);\n  y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);\n  z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);\n\n  l = (116 * y) - 16;\n  a = 500 * (x - y);\n  b = 200 * (y - z);\n\n  return [l, a, b];\n}\n\nfunction xyz2lch(args) {\n  return lab2lch(xyz2lab(args));\n}\n\nfunction lab2xyz(lab) {\n  var l = lab[0],\n      a = lab[1],\n      b = lab[2],\n      x, y, z, y2;\n\n  if (l <= 8) {\n    y = (l * 100) / 903.3;\n    y2 = (7.787 * (y / 100)) + (16 / 116);\n  } else {\n    y = 100 * Math.pow((l + 16) / 116, 3);\n    y2 = Math.pow(y / 100, 1/3);\n  }\n\n  x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3);\n\n  z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3);\n\n  return [x, y, z];\n}\n\nfunction lab2lch(lab) {\n  var l = lab[0],\n      a = lab[1],\n      b = lab[2],\n      hr, h, c;\n\n  hr = Math.atan2(b, a);\n  h = hr * 360 / 2 / Math.PI;\n  if (h < 0) {\n    h += 360;\n  }\n  c = Math.sqrt(a * a + b * b);\n  return [l, c, h];\n}\n\nfunction lab2rgb(args) {\n  return xyz2rgb(lab2xyz(args));\n}\n\nfunction lch2lab(lch) {\n  var l = lch[0],\n      c = lch[1],\n      h = lch[2],\n      a, b, hr;\n\n  hr = h / 360 * 2 * Math.PI;\n  a = c * Math.cos(hr);\n  b = c * Math.sin(hr);\n  return [l, a, b];\n}\n\nfunction lch2xyz(args) {\n  return lab2xyz(lch2lab(args));\n}\n\nfunction lch2rgb(args) {\n  return lab2rgb(lch2lab(args));\n}\n\nfunction keyword2rgb(keyword) {\n  return cssKeywords[keyword];\n}\n\nfunction keyword2hsl(args) {\n  return rgb2hsl(keyword2rgb(args));\n}\n\nfunction keyword2hsv(args) {\n  return rgb2hsv(keyword2rgb(args));\n}\n\nfunction keyword2hwb(args) {\n  return rgb2hwb(keyword2rgb(args));\n}\n\nfunction keyword2cmyk(args) {\n  return rgb2cmyk(keyword2rgb(args));\n}\n\nfunction keyword2lab(args) {\n  return rgb2lab(keyword2rgb(args));\n}\n\nfunction keyword2xyz(args) {\n  return rgb2xyz(keyword2rgb(args));\n}\n\nvar cssKeywords = {\n  aliceblue:  [240,248,255],\n  antiquewhite: [250,235,215],\n  aqua: [0,255,255],\n  aquamarine: [127,255,212],\n  azure:  [240,255,255],\n  beige:  [245,245,220],\n  bisque: [255,228,196],\n  black:  [0,0,0],\n  blanchedalmond: [255,235,205],\n  blue: [0,0,255],\n  blueviolet: [138,43,226],\n  brown:  [165,42,42],\n  burlywood:  [222,184,135],\n  cadetblue:  [95,158,160],\n  chartreuse: [127,255,0],\n  chocolate:  [210,105,30],\n  coral:  [255,127,80],\n  cornflowerblue: [100,149,237],\n  cornsilk: [255,248,220],\n  crimson:  [220,20,60],\n  cyan: [0,255,255],\n  darkblue: [0,0,139],\n  darkcyan: [0,139,139],\n  darkgoldenrod:  [184,134,11],\n  darkgray: [169,169,169],\n  darkgreen:  [0,100,0],\n  darkgrey: [169,169,169],\n  darkkhaki:  [189,183,107],\n  darkmagenta:  [139,0,139],\n  darkolivegreen: [85,107,47],\n  darkorange: [255,140,0],\n  darkorchid: [153,50,204],\n  darkred:  [139,0,0],\n  darksalmon: [233,150,122],\n  darkseagreen: [143,188,143],\n  darkslateblue:  [72,61,139],\n  darkslategray:  [47,79,79],\n  darkslategrey:  [47,79,79],\n  darkturquoise:  [0,206,209],\n  darkviolet: [148,0,211],\n  deeppink: [255,20,147],\n  deepskyblue:  [0,191,255],\n  dimgray:  [105,105,105],\n  dimgrey:  [105,105,105],\n  dodgerblue: [30,144,255],\n  firebrick:  [178,34,34],\n  floralwhite:  [255,250,240],\n  forestgreen:  [34,139,34],\n  fuchsia:  [255,0,255],\n  gainsboro:  [220,220,220],\n  ghostwhite: [248,248,255],\n  gold: [255,215,0],\n  goldenrod:  [218,165,32],\n  gray: [128,128,128],\n  green:  [0,128,0],\n  greenyellow:  [173,255,47],\n  grey: [128,128,128],\n  honeydew: [240,255,240],\n  hotpink:  [255,105,180],\n  indianred:  [205,92,92],\n  indigo: [75,0,130],\n  ivory:  [255,255,240],\n  khaki:  [240,230,140],\n  lavender: [230,230,250],\n  lavenderblush:  [255,240,245],\n  lawngreen:  [124,252,0],\n  lemonchiffon: [255,250,205],\n  lightblue:  [173,216,230],\n  lightcoral: [240,128,128],\n  lightcyan:  [224,255,255],\n  lightgoldenrodyellow: [250,250,210],\n  lightgray:  [211,211,211],\n  lightgreen: [144,238,144],\n  lightgrey:  [211,211,211],\n  lightpink:  [255,182,193],\n  lightsalmon:  [255,160,122],\n  lightseagreen:  [32,178,170],\n  lightskyblue: [135,206,250],\n  lightslategray: [119,136,153],\n  lightslategrey: [119,136,153],\n  lightsteelblue: [176,196,222],\n  lightyellow:  [255,255,224],\n  lime: [0,255,0],\n  limegreen:  [50,205,50],\n  linen:  [250,240,230],\n  magenta:  [255,0,255],\n  maroon: [128,0,0],\n  mediumaquamarine: [102,205,170],\n  mediumblue: [0,0,205],\n  mediumorchid: [186,85,211],\n  mediumpurple: [147,112,219],\n  mediumseagreen: [60,179,113],\n  mediumslateblue:  [123,104,238],\n  mediumspringgreen:  [0,250,154],\n  mediumturquoise:  [72,209,204],\n  mediumvioletred:  [199,21,133],\n  midnightblue: [25,25,112],\n  mintcream:  [245,255,250],\n  mistyrose:  [255,228,225],\n  moccasin: [255,228,181],\n  navajowhite:  [255,222,173],\n  navy: [0,0,128],\n  oldlace:  [253,245,230],\n  olive:  [128,128,0],\n  olivedrab:  [107,142,35],\n  orange: [255,165,0],\n  orangered:  [255,69,0],\n  orchid: [218,112,214],\n  palegoldenrod:  [238,232,170],\n  palegreen:  [152,251,152],\n  paleturquoise:  [175,238,238],\n  palevioletred:  [219,112,147],\n  papayawhip: [255,239,213],\n  peachpuff:  [255,218,185],\n  peru: [205,133,63],\n  pink: [255,192,203],\n  plum: [221,160,221],\n  powderblue: [176,224,230],\n  purple: [128,0,128],\n  rebeccapurple: [102, 51, 153],\n  red:  [255,0,0],\n  rosybrown:  [188,143,143],\n  royalblue:  [65,105,225],\n  saddlebrown:  [139,69,19],\n  salmon: [250,128,114],\n  sandybrown: [244,164,96],\n  seagreen: [46,139,87],\n  seashell: [255,245,238],\n  sienna: [160,82,45],\n  silver: [192,192,192],\n  skyblue:  [135,206,235],\n  slateblue:  [106,90,205],\n  slategray:  [112,128,144],\n  slategrey:  [112,128,144],\n  snow: [255,250,250],\n  springgreen:  [0,255,127],\n  steelblue:  [70,130,180],\n  tan:  [210,180,140],\n  teal: [0,128,128],\n  thistle:  [216,191,216],\n  tomato: [255,99,71],\n  turquoise:  [64,224,208],\n  violet: [238,130,238],\n  wheat:  [245,222,179],\n  white:  [255,255,255],\n  whitesmoke: [245,245,245],\n  yellow: [255,255,0],\n  yellowgreen:  [154,205,50]\n};\n\nvar reverseKeywords = {};\nfor (var key in cssKeywords) {\n  reverseKeywords[JSON.stringify(cssKeywords[key])] = key;\n}\n\n},{}],5:[function(require,module,exports){\nvar conversions = require(4);\n\nvar convert = function() {\n   return new Converter();\n}\n\nfor (var func in conversions) {\n  // export Raw versions\n  convert[func + \"Raw\"] =  (function(func) {\n    // accept array or plain args\n    return function(arg) {\n      if (typeof arg == \"number\")\n        arg = Array.prototype.slice.call(arguments);\n      return conversions[func](arg);\n    }\n  })(func);\n\n  var pair = /(\\w+)2(\\w+)/.exec(func),\n      from = pair[1],\n      to = pair[2];\n\n  // export rgb2hsl and [\"rgb\"][\"hsl\"]\n  convert[from] = convert[from] || {};\n\n  convert[from][to] = convert[func] = (function(func) { \n    return function(arg) {\n      if (typeof arg == \"number\")\n        arg = Array.prototype.slice.call(arguments);\n      \n      var val = conversions[func](arg);\n      if (typeof val == \"string\" || val === undefined)\n        return val; // keyword\n\n      for (var i = 0; i < val.length; i++)\n        val[i] = Math.round(val[i]);\n      return val;\n    }\n  })(func);\n}\n\n\n/* Converter does lazy conversion and caching */\nvar Converter = function() {\n   this.convs = {};\n};\n\n/* Either get the values for a space or\n  set the values for a space, depending on args */\nConverter.prototype.routeSpace = function(space, args) {\n   var values = args[0];\n   if (values === undefined) {\n      // color.rgb()\n      return this.getValues(space);\n   }\n   // color.rgb(10, 10, 10)\n   if (typeof values == \"number\") {\n      values = Array.prototype.slice.call(args);        \n   }\n\n   return this.setValues(space, values);\n};\n  \n/* Set the values for a space, invalidating cache */\nConverter.prototype.setValues = function(space, values) {\n   this.space = space;\n   this.convs = {};\n   this.convs[space] = values;\n   return this;\n};\n\n/* Get the values for a space. If there's already\n  a conversion for the space, fetch it, otherwise\n  compute it */\nConverter.prototype.getValues = function(space) {\n   var vals = this.convs[space];\n   if (!vals) {\n      var fspace = this.space,\n          from = this.convs[fspace];\n      vals = convert[fspace][space](from);\n\n      this.convs[space] = vals;\n   }\n  return vals;\n};\n\n[\"rgb\", \"hsl\", \"hsv\", \"cmyk\", \"keyword\"].forEach(function(space) {\n   Converter.prototype[space] = function(vals) {\n      return this.routeSpace(space, arguments);\n   }\n});\n\nmodule.exports = convert;\n},{\"4\":4}],6:[function(require,module,exports){\n'use strict'\r\n\r\nmodule.exports = {\r\n\t\"aliceblue\": [240, 248, 255],\r\n\t\"antiquewhite\": [250, 235, 215],\r\n\t\"aqua\": [0, 255, 255],\r\n\t\"aquamarine\": [127, 255, 212],\r\n\t\"azure\": [240, 255, 255],\r\n\t\"beige\": [245, 245, 220],\r\n\t\"bisque\": [255, 228, 196],\r\n\t\"black\": [0, 0, 0],\r\n\t\"blanchedalmond\": [255, 235, 205],\r\n\t\"blue\": [0, 0, 255],\r\n\t\"blueviolet\": [138, 43, 226],\r\n\t\"brown\": [165, 42, 42],\r\n\t\"burlywood\": [222, 184, 135],\r\n\t\"cadetblue\": [95, 158, 160],\r\n\t\"chartreuse\": [127, 255, 0],\r\n\t\"chocolate\": [210, 105, 30],\r\n\t\"coral\": [255, 127, 80],\r\n\t\"cornflowerblue\": [100, 149, 237],\r\n\t\"cornsilk\": [255, 248, 220],\r\n\t\"crimson\": [220, 20, 60],\r\n\t\"cyan\": [0, 255, 255],\r\n\t\"darkblue\": [0, 0, 139],\r\n\t\"darkcyan\": [0, 139, 139],\r\n\t\"darkgoldenrod\": [184, 134, 11],\r\n\t\"darkgray\": [169, 169, 169],\r\n\t\"darkgreen\": [0, 100, 0],\r\n\t\"darkgrey\": [169, 169, 169],\r\n\t\"darkkhaki\": [189, 183, 107],\r\n\t\"darkmagenta\": [139, 0, 139],\r\n\t\"darkolivegreen\": [85, 107, 47],\r\n\t\"darkorange\": [255, 140, 0],\r\n\t\"darkorchid\": [153, 50, 204],\r\n\t\"darkred\": [139, 0, 0],\r\n\t\"darksalmon\": [233, 150, 122],\r\n\t\"darkseagreen\": [143, 188, 143],\r\n\t\"darkslateblue\": [72, 61, 139],\r\n\t\"darkslategray\": [47, 79, 79],\r\n\t\"darkslategrey\": [47, 79, 79],\r\n\t\"darkturquoise\": [0, 206, 209],\r\n\t\"darkviolet\": [148, 0, 211],\r\n\t\"deeppink\": [255, 20, 147],\r\n\t\"deepskyblue\": [0, 191, 255],\r\n\t\"dimgray\": [105, 105, 105],\r\n\t\"dimgrey\": [105, 105, 105],\r\n\t\"dodgerblue\": [30, 144, 255],\r\n\t\"firebrick\": [178, 34, 34],\r\n\t\"floralwhite\": [255, 250, 240],\r\n\t\"forestgreen\": [34, 139, 34],\r\n\t\"fuchsia\": [255, 0, 255],\r\n\t\"gainsboro\": [220, 220, 220],\r\n\t\"ghostwhite\": [248, 248, 255],\r\n\t\"gold\": [255, 215, 0],\r\n\t\"goldenrod\": [218, 165, 32],\r\n\t\"gray\": [128, 128, 128],\r\n\t\"green\": [0, 128, 0],\r\n\t\"greenyellow\": [173, 255, 47],\r\n\t\"grey\": [128, 128, 128],\r\n\t\"honeydew\": [240, 255, 240],\r\n\t\"hotpink\": [255, 105, 180],\r\n\t\"indianred\": [205, 92, 92],\r\n\t\"indigo\": [75, 0, 130],\r\n\t\"ivory\": [255, 255, 240],\r\n\t\"khaki\": [240, 230, 140],\r\n\t\"lavender\": [230, 230, 250],\r\n\t\"lavenderblush\": [255, 240, 245],\r\n\t\"lawngreen\": [124, 252, 0],\r\n\t\"lemonchiffon\": [255, 250, 205],\r\n\t\"lightblue\": [173, 216, 230],\r\n\t\"lightcoral\": [240, 128, 128],\r\n\t\"lightcyan\": [224, 255, 255],\r\n\t\"lightgoldenrodyellow\": [250, 250, 210],\r\n\t\"lightgray\": [211, 211, 211],\r\n\t\"lightgreen\": [144, 238, 144],\r\n\t\"lightgrey\": [211, 211, 211],\r\n\t\"lightpink\": [255, 182, 193],\r\n\t\"lightsalmon\": [255, 160, 122],\r\n\t\"lightseagreen\": [32, 178, 170],\r\n\t\"lightskyblue\": [135, 206, 250],\r\n\t\"lightslategray\": [119, 136, 153],\r\n\t\"lightslategrey\": [119, 136, 153],\r\n\t\"lightsteelblue\": [176, 196, 222],\r\n\t\"lightyellow\": [255, 255, 224],\r\n\t\"lime\": [0, 255, 0],\r\n\t\"limegreen\": [50, 205, 50],\r\n\t\"linen\": [250, 240, 230],\r\n\t\"magenta\": [255, 0, 255],\r\n\t\"maroon\": [128, 0, 0],\r\n\t\"mediumaquamarine\": [102, 205, 170],\r\n\t\"mediumblue\": [0, 0, 205],\r\n\t\"mediumorchid\": [186, 85, 211],\r\n\t\"mediumpurple\": [147, 112, 219],\r\n\t\"mediumseagreen\": [60, 179, 113],\r\n\t\"mediumslateblue\": [123, 104, 238],\r\n\t\"mediumspringgreen\": [0, 250, 154],\r\n\t\"mediumturquoise\": [72, 209, 204],\r\n\t\"mediumvioletred\": [199, 21, 133],\r\n\t\"midnightblue\": [25, 25, 112],\r\n\t\"mintcream\": [245, 255, 250],\r\n\t\"mistyrose\": [255, 228, 225],\r\n\t\"moccasin\": [255, 228, 181],\r\n\t\"navajowhite\": [255, 222, 173],\r\n\t\"navy\": [0, 0, 128],\r\n\t\"oldlace\": [253, 245, 230],\r\n\t\"olive\": [128, 128, 0],\r\n\t\"olivedrab\": [107, 142, 35],\r\n\t\"orange\": [255, 165, 0],\r\n\t\"orangered\": [255, 69, 0],\r\n\t\"orchid\": [218, 112, 214],\r\n\t\"palegoldenrod\": [238, 232, 170],\r\n\t\"palegreen\": [152, 251, 152],\r\n\t\"paleturquoise\": [175, 238, 238],\r\n\t\"palevioletred\": [219, 112, 147],\r\n\t\"papayawhip\": [255, 239, 213],\r\n\t\"peachpuff\": [255, 218, 185],\r\n\t\"peru\": [205, 133, 63],\r\n\t\"pink\": [255, 192, 203],\r\n\t\"plum\": [221, 160, 221],\r\n\t\"powderblue\": [176, 224, 230],\r\n\t\"purple\": [128, 0, 128],\r\n\t\"rebeccapurple\": [102, 51, 153],\r\n\t\"red\": [255, 0, 0],\r\n\t\"rosybrown\": [188, 143, 143],\r\n\t\"royalblue\": [65, 105, 225],\r\n\t\"saddlebrown\": [139, 69, 19],\r\n\t\"salmon\": [250, 128, 114],\r\n\t\"sandybrown\": [244, 164, 96],\r\n\t\"seagreen\": [46, 139, 87],\r\n\t\"seashell\": [255, 245, 238],\r\n\t\"sienna\": [160, 82, 45],\r\n\t\"silver\": [192, 192, 192],\r\n\t\"skyblue\": [135, 206, 235],\r\n\t\"slateblue\": [106, 90, 205],\r\n\t\"slategray\": [112, 128, 144],\r\n\t\"slategrey\": [112, 128, 144],\r\n\t\"snow\": [255, 250, 250],\r\n\t\"springgreen\": [0, 255, 127],\r\n\t\"steelblue\": [70, 130, 180],\r\n\t\"tan\": [210, 180, 140],\r\n\t\"teal\": [0, 128, 128],\r\n\t\"thistle\": [216, 191, 216],\r\n\t\"tomato\": [255, 99, 71],\r\n\t\"turquoise\": [64, 224, 208],\r\n\t\"violet\": [238, 130, 238],\r\n\t\"wheat\": [245, 222, 179],\r\n\t\"white\": [255, 255, 255],\r\n\t\"whitesmoke\": [245, 245, 245],\r\n\t\"yellow\": [255, 255, 0],\r\n\t\"yellowgreen\": [154, 205, 50]\r\n};\r\n\n},{}],7:[function(require,module,exports){\n/**\n * @namespace Chart\n */\nvar Chart = require(29)();\n\nChart.helpers = require(45);\n\n// @todo dispatch these helpers into appropriated helpers/helpers.* file and write unit tests!\nrequire(27)(Chart);\n\nChart.defaults = require(25);\nChart.Element = require(26);\nChart.elements = require(40);\nChart.Interaction = require(28);\nChart.layouts = require(30);\nChart.platform = require(48);\nChart.plugins = require(31);\nChart.Ticks = require(34);\n\nrequire(22)(Chart);\nrequire(23)(Chart);\nrequire(24)(Chart);\nrequire(33)(Chart);\nrequire(32)(Chart);\nrequire(35)(Chart);\n\nrequire(55)(Chart);\nrequire(53)(Chart);\nrequire(54)(Chart);\nrequire(56)(Chart);\nrequire(57)(Chart);\nrequire(58)(Chart);\n\n// Controllers must be loaded after elements\n// See Chart.core.datasetController.dataElementType\nrequire(15)(Chart);\nrequire(16)(Chart);\nrequire(17)(Chart);\nrequire(18)(Chart);\nrequire(19)(Chart);\nrequire(20)(Chart);\nrequire(21)(Chart);\n\nrequire(8)(Chart);\nrequire(9)(Chart);\nrequire(10)(Chart);\nrequire(11)(Chart);\nrequire(12)(Chart);\nrequire(13)(Chart);\nrequire(14)(Chart);\n\n// Loading built-it plugins\nvar plugins = require(49);\nfor (var k in plugins) {\n\tif (plugins.hasOwnProperty(k)) {\n\t\tChart.plugins.register(plugins[k]);\n\t}\n}\n\nChart.platform.initialize();\n\nmodule.exports = Chart;\nif (typeof window !== 'undefined') {\n\twindow.Chart = Chart;\n}\n\n// DEPRECATIONS\n\n/**\n * Provided for backward compatibility, not available anymore\n * @namespace Chart.Legend\n * @deprecated since version 2.1.5\n * @todo remove at version 3\n * @private\n */\nChart.Legend = plugins.legend._element;\n\n/**\n * Provided for backward compatibility, not available anymore\n * @namespace Chart.Title\n * @deprecated since version 2.1.5\n * @todo remove at version 3\n * @private\n */\nChart.Title = plugins.title._element;\n\n/**\n * Provided for backward compatibility, use Chart.plugins instead\n * @namespace Chart.pluginService\n * @deprecated since version 2.1.5\n * @todo remove at version 3\n * @private\n */\nChart.pluginService = Chart.plugins;\n\n/**\n * Provided for backward compatibility, inheriting from Chart.PlugingBase has no\n * effect, instead simply create/register plugins via plain JavaScript objects.\n * @interface Chart.PluginBase\n * @deprecated since version 2.5.0\n * @todo remove at version 3\n * @private\n */\nChart.PluginBase = Chart.Element.extend({});\n\n/**\n * Provided for backward compatibility, use Chart.helpers.canvas instead.\n * @namespace Chart.canvasHelpers\n * @deprecated since version 2.6.0\n * @todo remove at version 3\n * @private\n */\nChart.canvasHelpers = Chart.helpers.canvas;\n\n/**\n * Provided for backward compatibility, use Chart.layouts instead.\n * @namespace Chart.layoutService\n * @deprecated since version 2.8.0\n * @todo remove at version 3\n * @private\n */\nChart.layoutService = Chart.layouts;\n\n},{\"10\":10,\"11\":11,\"12\":12,\"13\":13,\"14\":14,\"15\":15,\"16\":16,\"17\":17,\"18\":18,\"19\":19,\"20\":20,\"21\":21,\"22\":22,\"23\":23,\"24\":24,\"25\":25,\"26\":26,\"27\":27,\"28\":28,\"29\":29,\"30\":30,\"31\":31,\"32\":32,\"33\":33,\"34\":34,\"35\":35,\"40\":40,\"45\":45,\"48\":48,\"49\":49,\"53\":53,\"54\":54,\"55\":55,\"56\":56,\"57\":57,\"58\":58,\"8\":8,\"9\":9}],8:[function(require,module,exports){\n'use strict';\n\nmodule.exports = function(Chart) {\n\n\tChart.Bar = function(context, config) {\n\t\tconfig.type = 'bar';\n\n\t\treturn new Chart(context, config);\n\t};\n\n};\n\n},{}],9:[function(require,module,exports){\n'use strict';\n\nmodule.exports = function(Chart) {\n\n\tChart.Bubble = function(context, config) {\n\t\tconfig.type = 'bubble';\n\t\treturn new Chart(context, config);\n\t};\n\n};\n\n},{}],10:[function(require,module,exports){\n'use strict';\n\nmodule.exports = function(Chart) {\n\n\tChart.Doughnut = function(context, config) {\n\t\tconfig.type = 'doughnut';\n\n\t\treturn new Chart(context, config);\n\t};\n\n};\n\n},{}],11:[function(require,module,exports){\n'use strict';\n\nmodule.exports = function(Chart) {\n\n\tChart.Line = function(context, config) {\n\t\tconfig.type = 'line';\n\n\t\treturn new Chart(context, config);\n\t};\n\n};\n\n},{}],12:[function(require,module,exports){\n'use strict';\n\nmodule.exports = function(Chart) {\n\n\tChart.PolarArea = function(context, config) {\n\t\tconfig.type = 'polarArea';\n\n\t\treturn new Chart(context, config);\n\t};\n\n};\n\n},{}],13:[function(require,module,exports){\n'use strict';\n\nmodule.exports = function(Chart) {\n\n\tChart.Radar = function(context, config) {\n\t\tconfig.type = 'radar';\n\n\t\treturn new Chart(context, config);\n\t};\n\n};\n\n},{}],14:[function(require,module,exports){\n'use strict';\n\nmodule.exports = function(Chart) {\n\tChart.Scatter = function(context, config) {\n\t\tconfig.type = 'scatter';\n\t\treturn new Chart(context, config);\n\t};\n};\n\n},{}],15:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\nvar elements = require(40);\nvar helpers = require(45);\n\ndefaults._set('bar', {\n\thover: {\n\t\tmode: 'label'\n\t},\n\n\tscales: {\n\t\txAxes: [{\n\t\t\ttype: 'category',\n\n\t\t\t// Specific to Bar Controller\n\t\t\tcategoryPercentage: 0.8,\n\t\t\tbarPercentage: 0.9,\n\n\t\t\t// offset settings\n\t\t\toffset: true,\n\n\t\t\t// grid line settings\n\t\t\tgridLines: {\n\t\t\t\toffsetGridLines: true\n\t\t\t}\n\t\t}],\n\n\t\tyAxes: [{\n\t\t\ttype: 'linear'\n\t\t}]\n\t}\n});\n\ndefaults._set('horizontalBar', {\n\thover: {\n\t\tmode: 'index',\n\t\taxis: 'y'\n\t},\n\n\tscales: {\n\t\txAxes: [{\n\t\t\ttype: 'linear',\n\t\t\tposition: 'bottom'\n\t\t}],\n\n\t\tyAxes: [{\n\t\t\tposition: 'left',\n\t\t\ttype: 'category',\n\n\t\t\t// Specific to Horizontal Bar Controller\n\t\t\tcategoryPercentage: 0.8,\n\t\t\tbarPercentage: 0.9,\n\n\t\t\t// offset settings\n\t\t\toffset: true,\n\n\t\t\t// grid line settings\n\t\t\tgridLines: {\n\t\t\t\toffsetGridLines: true\n\t\t\t}\n\t\t}]\n\t},\n\n\telements: {\n\t\trectangle: {\n\t\t\tborderSkipped: 'left'\n\t\t}\n\t},\n\n\ttooltips: {\n\t\tcallbacks: {\n\t\t\ttitle: function(item, data) {\n\t\t\t\t// Pick first xLabel for now\n\t\t\t\tvar title = '';\n\n\t\t\t\tif (item.length > 0) {\n\t\t\t\t\tif (item[0].yLabel) {\n\t\t\t\t\t\ttitle = item[0].yLabel;\n\t\t\t\t\t} else if (data.labels.length > 0 && item[0].index < data.labels.length) {\n\t\t\t\t\t\ttitle = data.labels[item[0].index];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn title;\n\t\t\t},\n\n\t\t\tlabel: function(item, data) {\n\t\t\t\tvar datasetLabel = data.datasets[item.datasetIndex].label || '';\n\t\t\t\treturn datasetLabel + ': ' + item.xLabel;\n\t\t\t}\n\t\t},\n\t\tmode: 'index',\n\t\taxis: 'y'\n\t}\n});\n\n/**\n * Computes the \"optimal\" sample size to maintain bars equally sized while preventing overlap.\n * @private\n */\nfunction computeMinSampleSize(scale, pixels) {\n\tvar min = scale.isHorizontal() ? scale.width : scale.height;\n\tvar ticks = scale.getTicks();\n\tvar prev, curr, i, ilen;\n\n\tfor (i = 1, ilen = pixels.length; i < ilen; ++i) {\n\t\tmin = Math.min(min, pixels[i] - pixels[i - 1]);\n\t}\n\n\tfor (i = 0, ilen = ticks.length; i < ilen; ++i) {\n\t\tcurr = scale.getPixelForTick(i);\n\t\tmin = i > 0 ? Math.min(min, curr - prev) : min;\n\t\tprev = curr;\n\t}\n\n\treturn min;\n}\n\n/**\n * Computes an \"ideal\" category based on the absolute bar thickness or, if undefined or null,\n * uses the smallest interval (see computeMinSampleSize) that prevents bar overlapping. This\n * mode currently always generates bars equally sized (until we introduce scriptable options?).\n * @private\n */\nfunction computeFitCategoryTraits(index, ruler, options) {\n\tvar thickness = options.barThickness;\n\tvar count = ruler.stackCount;\n\tvar curr = ruler.pixels[index];\n\tvar size, ratio;\n\n\tif (helpers.isNullOrUndef(thickness)) {\n\t\tsize = ruler.min * options.categoryPercentage;\n\t\tratio = options.barPercentage;\n\t} else {\n\t\t// When bar thickness is enforced, category and bar percentages are ignored.\n\t\t// Note(SB): we could add support for relative bar thickness (e.g. barThickness: '50%')\n\t\t// and deprecate barPercentage since this value is ignored when thickness is absolute.\n\t\tsize = thickness * count;\n\t\tratio = 1;\n\t}\n\n\treturn {\n\t\tchunk: size / count,\n\t\tratio: ratio,\n\t\tstart: curr - (size / 2)\n\t};\n}\n\n/**\n * Computes an \"optimal\" category that globally arranges bars side by side (no gap when\n * percentage options are 1), based on the previous and following categories. This mode\n * generates bars with different widths when data are not evenly spaced.\n * @private\n */\nfunction computeFlexCategoryTraits(index, ruler, options) {\n\tvar pixels = ruler.pixels;\n\tvar curr = pixels[index];\n\tvar prev = index > 0 ? pixels[index - 1] : null;\n\tvar next = index < pixels.length - 1 ? pixels[index + 1] : null;\n\tvar percent = options.categoryPercentage;\n\tvar start, size;\n\n\tif (prev === null) {\n\t\t// first data: its size is double based on the next point or,\n\t\t// if it's also the last data, we use the scale end extremity.\n\t\tprev = curr - (next === null ? ruler.end - curr : next - curr);\n\t}\n\n\tif (next === null) {\n\t\t// last data: its size is also double based on the previous point.\n\t\tnext = curr + curr - prev;\n\t}\n\n\tstart = curr - ((curr - prev) / 2) * percent;\n\tsize = ((next - prev) / 2) * percent;\n\n\treturn {\n\t\tchunk: size / ruler.stackCount,\n\t\tratio: options.barPercentage,\n\t\tstart: start\n\t};\n}\n\nmodule.exports = function(Chart) {\n\n\tChart.controllers.bar = Chart.DatasetController.extend({\n\n\t\tdataElementType: elements.Rectangle,\n\n\t\tinitialize: function() {\n\t\t\tvar me = this;\n\t\t\tvar meta;\n\n\t\t\tChart.DatasetController.prototype.initialize.apply(me, arguments);\n\n\t\t\tmeta = me.getMeta();\n\t\t\tmeta.stack = me.getDataset().stack;\n\t\t\tmeta.bar = true;\n\t\t},\n\n\t\tupdate: function(reset) {\n\t\t\tvar me = this;\n\t\t\tvar rects = me.getMeta().data;\n\t\t\tvar i, ilen;\n\n\t\t\tme._ruler = me.getRuler();\n\n\t\t\tfor (i = 0, ilen = rects.length; i < ilen; ++i) {\n\t\t\t\tme.updateElement(rects[i], i, reset);\n\t\t\t}\n\t\t},\n\n\t\tupdateElement: function(rectangle, index, reset) {\n\t\t\tvar me = this;\n\t\t\tvar chart = me.chart;\n\t\t\tvar meta = me.getMeta();\n\t\t\tvar dataset = me.getDataset();\n\t\t\tvar custom = rectangle.custom || {};\n\t\t\tvar rectangleOptions = chart.options.elements.rectangle;\n\n\t\t\trectangle._xScale = me.getScaleForId(meta.xAxisID);\n\t\t\trectangle._yScale = me.getScaleForId(meta.yAxisID);\n\t\t\trectangle._datasetIndex = me.index;\n\t\t\trectangle._index = index;\n\n\t\t\trectangle._model = {\n\t\t\t\tdatasetLabel: dataset.label,\n\t\t\t\tlabel: chart.data.labels[index],\n\t\t\t\tborderSkipped: custom.borderSkipped ? custom.borderSkipped : rectangleOptions.borderSkipped,\n\t\t\t\tbackgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleOptions.backgroundColor),\n\t\t\t\tborderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleOptions.borderColor),\n\t\t\t\tborderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleOptions.borderWidth)\n\t\t\t};\n\n\t\t\tme.updateElementGeometry(rectangle, index, reset);\n\n\t\t\trectangle.pivot();\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tupdateElementGeometry: function(rectangle, index, reset) {\n\t\t\tvar me = this;\n\t\t\tvar model = rectangle._model;\n\t\t\tvar vscale = me.getValueScale();\n\t\t\tvar base = vscale.getBasePixel();\n\t\t\tvar horizontal = vscale.isHorizontal();\n\t\t\tvar ruler = me._ruler || me.getRuler();\n\t\t\tvar vpixels = me.calculateBarValuePixels(me.index, index);\n\t\t\tvar ipixels = me.calculateBarIndexPixels(me.index, index, ruler);\n\n\t\t\tmodel.horizontal = horizontal;\n\t\t\tmodel.base = reset ? base : vpixels.base;\n\t\t\tmodel.x = horizontal ? reset ? base : vpixels.head : ipixels.center;\n\t\t\tmodel.y = horizontal ? ipixels.center : reset ? base : vpixels.head;\n\t\t\tmodel.height = horizontal ? ipixels.size : undefined;\n\t\t\tmodel.width = horizontal ? undefined : ipixels.size;\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tgetValueScaleId: function() {\n\t\t\treturn this.getMeta().yAxisID;\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tgetIndexScaleId: function() {\n\t\t\treturn this.getMeta().xAxisID;\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tgetValueScale: function() {\n\t\t\treturn this.getScaleForId(this.getValueScaleId());\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tgetIndexScale: function() {\n\t\t\treturn this.getScaleForId(this.getIndexScaleId());\n\t\t},\n\n\t\t/**\n\t\t * Returns the stacks based on groups and bar visibility.\n\t\t * @param {Number} [last] - The dataset index\n\t\t * @returns {Array} The stack list\n\t\t * @private\n\t\t */\n\t\t_getStacks: function(last) {\n\t\t\tvar me = this;\n\t\t\tvar chart = me.chart;\n\t\t\tvar scale = me.getIndexScale();\n\t\t\tvar stacked = scale.options.stacked;\n\t\t\tvar ilen = last === undefined ? chart.data.datasets.length : last + 1;\n\t\t\tvar stacks = [];\n\t\t\tvar i, meta;\n\n\t\t\tfor (i = 0; i < ilen; ++i) {\n\t\t\t\tmeta = chart.getDatasetMeta(i);\n\t\t\t\tif (meta.bar && chart.isDatasetVisible(i) &&\n\t\t\t\t\t(stacked === false ||\n\t\t\t\t\t(stacked === true && stacks.indexOf(meta.stack) === -1) ||\n\t\t\t\t\t(stacked === undefined && (meta.stack === undefined || stacks.indexOf(meta.stack) === -1)))) {\n\t\t\t\t\tstacks.push(meta.stack);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn stacks;\n\t\t},\n\n\t\t/**\n\t\t * Returns the effective number of stacks based on groups and bar visibility.\n\t\t * @private\n\t\t */\n\t\tgetStackCount: function() {\n\t\t\treturn this._getStacks().length;\n\t\t},\n\n\t\t/**\n\t\t * Returns the stack index for the given dataset based on groups and bar visibility.\n\t\t * @param {Number} [datasetIndex] - The dataset index\n\t\t * @param {String} [name] - The stack name to find\n\t\t * @returns {Number} The stack index\n\t\t * @private\n\t\t */\n\t\tgetStackIndex: function(datasetIndex, name) {\n\t\t\tvar stacks = this._getStacks(datasetIndex);\n\t\t\tvar index = (name !== undefined)\n\t\t\t\t? stacks.indexOf(name)\n\t\t\t\t: -1; // indexOf returns -1 if element is not present\n\n\t\t\treturn (index === -1)\n\t\t\t\t? stacks.length - 1\n\t\t\t\t: index;\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tgetRuler: function() {\n\t\t\tvar me = this;\n\t\t\tvar scale = me.getIndexScale();\n\t\t\tvar stackCount = me.getStackCount();\n\t\t\tvar datasetIndex = me.index;\n\t\t\tvar isHorizontal = scale.isHorizontal();\n\t\t\tvar start = isHorizontal ? scale.left : scale.top;\n\t\t\tvar end = start + (isHorizontal ? scale.width : scale.height);\n\t\t\tvar pixels = [];\n\t\t\tvar i, ilen, min;\n\n\t\t\tfor (i = 0, ilen = me.getMeta().data.length; i < ilen; ++i) {\n\t\t\t\tpixels.push(scale.getPixelForValue(null, i, datasetIndex));\n\t\t\t}\n\n\t\t\tmin = helpers.isNullOrUndef(scale.options.barThickness)\n\t\t\t\t? computeMinSampleSize(scale, pixels)\n\t\t\t\t: -1;\n\n\t\t\treturn {\n\t\t\t\tmin: min,\n\t\t\t\tpixels: pixels,\n\t\t\t\tstart: start,\n\t\t\t\tend: end,\n\t\t\t\tstackCount: stackCount,\n\t\t\t\tscale: scale\n\t\t\t};\n\t\t},\n\n\t\t/**\n\t\t * Note: pixel values are not clamped to the scale area.\n\t\t * @private\n\t\t */\n\t\tcalculateBarValuePixels: function(datasetIndex, index) {\n\t\t\tvar me = this;\n\t\t\tvar chart = me.chart;\n\t\t\tvar meta = me.getMeta();\n\t\t\tvar scale = me.getValueScale();\n\t\t\tvar datasets = chart.data.datasets;\n\t\t\tvar value = scale.getRightValue(datasets[datasetIndex].data[index]);\n\t\t\tvar stacked = scale.options.stacked;\n\t\t\tvar stack = meta.stack;\n\t\t\tvar start = 0;\n\t\t\tvar i, imeta, ivalue, base, head, size;\n\n\t\t\tif (stacked || (stacked === undefined && stack !== undefined)) {\n\t\t\t\tfor (i = 0; i < datasetIndex; ++i) {\n\t\t\t\t\timeta = chart.getDatasetMeta(i);\n\n\t\t\t\t\tif (imeta.bar &&\n\t\t\t\t\t\timeta.stack === stack &&\n\t\t\t\t\t\timeta.controller.getValueScaleId() === scale.id &&\n\t\t\t\t\t\tchart.isDatasetVisible(i)) {\n\n\t\t\t\t\t\tivalue = scale.getRightValue(datasets[i].data[index]);\n\t\t\t\t\t\tif ((value < 0 && ivalue < 0) || (value >= 0 && ivalue > 0)) {\n\t\t\t\t\t\t\tstart += ivalue;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tbase = scale.getPixelForValue(start);\n\t\t\thead = scale.getPixelForValue(start + value);\n\t\t\tsize = (head - base) / 2;\n\n\t\t\treturn {\n\t\t\t\tsize: size,\n\t\t\t\tbase: base,\n\t\t\t\thead: head,\n\t\t\t\tcenter: head + size / 2\n\t\t\t};\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tcalculateBarIndexPixels: function(datasetIndex, index, ruler) {\n\t\t\tvar me = this;\n\t\t\tvar options = ruler.scale.options;\n\t\t\tvar range = options.barThickness === 'flex'\n\t\t\t\t? computeFlexCategoryTraits(index, ruler, options)\n\t\t\t\t: computeFitCategoryTraits(index, ruler, options);\n\n\t\t\tvar stackIndex = me.getStackIndex(datasetIndex, me.getMeta().stack);\n\t\t\tvar center = range.start + (range.chunk * stackIndex) + (range.chunk / 2);\n\t\t\tvar size = Math.min(\n\t\t\t\thelpers.valueOrDefault(options.maxBarThickness, Infinity),\n\t\t\t\trange.chunk * range.ratio);\n\n\t\t\treturn {\n\t\t\t\tbase: center - size / 2,\n\t\t\t\thead: center + size / 2,\n\t\t\t\tcenter: center,\n\t\t\t\tsize: size\n\t\t\t};\n\t\t},\n\n\t\tdraw: function() {\n\t\t\tvar me = this;\n\t\t\tvar chart = me.chart;\n\t\t\tvar scale = me.getValueScale();\n\t\t\tvar rects = me.getMeta().data;\n\t\t\tvar dataset = me.getDataset();\n\t\t\tvar ilen = rects.length;\n\t\t\tvar i = 0;\n\n\t\t\thelpers.canvas.clipArea(chart.ctx, chart.chartArea);\n\n\t\t\tfor (; i < ilen; ++i) {\n\t\t\t\tif (!isNaN(scale.getRightValue(dataset.data[i]))) {\n\t\t\t\t\trects[i].draw();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\thelpers.canvas.unclipArea(chart.ctx);\n\t\t},\n\n\t\tsetHoverStyle: function(rectangle) {\n\t\t\tvar dataset = this.chart.data.datasets[rectangle._datasetIndex];\n\t\t\tvar index = rectangle._index;\n\t\t\tvar custom = rectangle.custom || {};\n\t\t\tvar model = rectangle._model;\n\n\t\t\tmodel.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));\n\t\t\tmodel.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.getHoverColor(model.borderColor));\n\t\t\tmodel.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);\n\t\t},\n\n\t\tremoveHoverStyle: function(rectangle) {\n\t\t\tvar dataset = this.chart.data.datasets[rectangle._datasetIndex];\n\t\t\tvar index = rectangle._index;\n\t\t\tvar custom = rectangle.custom || {};\n\t\t\tvar model = rectangle._model;\n\t\t\tvar rectangleElementOptions = this.chart.options.elements.rectangle;\n\n\t\t\tmodel.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.backgroundColor, index, rectangleElementOptions.backgroundColor);\n\t\t\tmodel.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.borderColor, index, rectangleElementOptions.borderColor);\n\t\t\tmodel.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.borderWidth, index, rectangleElementOptions.borderWidth);\n\t\t}\n\t});\n\n\tChart.controllers.horizontalBar = Chart.controllers.bar.extend({\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tgetValueScaleId: function() {\n\t\t\treturn this.getMeta().xAxisID;\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tgetIndexScaleId: function() {\n\t\t\treturn this.getMeta().yAxisID;\n\t\t}\n\t});\n};\n\n},{\"25\":25,\"40\":40,\"45\":45}],16:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\nvar elements = require(40);\nvar helpers = require(45);\n\ndefaults._set('bubble', {\n\thover: {\n\t\tmode: 'single'\n\t},\n\n\tscales: {\n\t\txAxes: [{\n\t\t\ttype: 'linear', // bubble should probably use a linear scale by default\n\t\t\tposition: 'bottom',\n\t\t\tid: 'x-axis-0' // need an ID so datasets can reference the scale\n\t\t}],\n\t\tyAxes: [{\n\t\t\ttype: 'linear',\n\t\t\tposition: 'left',\n\t\t\tid: 'y-axis-0'\n\t\t}]\n\t},\n\n\ttooltips: {\n\t\tcallbacks: {\n\t\t\ttitle: function() {\n\t\t\t\t// Title doesn't make sense for scatter since we format the data as a point\n\t\t\t\treturn '';\n\t\t\t},\n\t\t\tlabel: function(item, data) {\n\t\t\t\tvar datasetLabel = data.datasets[item.datasetIndex].label || '';\n\t\t\t\tvar dataPoint = data.datasets[item.datasetIndex].data[item.index];\n\t\t\t\treturn datasetLabel + ': (' + item.xLabel + ', ' + item.yLabel + ', ' + dataPoint.r + ')';\n\t\t\t}\n\t\t}\n\t}\n});\n\n\nmodule.exports = function(Chart) {\n\n\tChart.controllers.bubble = Chart.DatasetController.extend({\n\t\t/**\n\t\t * @protected\n\t\t */\n\t\tdataElementType: elements.Point,\n\n\t\t/**\n\t\t * @protected\n\t\t */\n\t\tupdate: function(reset) {\n\t\t\tvar me = this;\n\t\t\tvar meta = me.getMeta();\n\t\t\tvar points = meta.data;\n\n\t\t\t// Update Points\n\t\t\thelpers.each(points, function(point, index) {\n\t\t\t\tme.updateElement(point, index, reset);\n\t\t\t});\n\t\t},\n\n\t\t/**\n\t\t * @protected\n\t\t */\n\t\tupdateElement: function(point, index, reset) {\n\t\t\tvar me = this;\n\t\t\tvar meta = me.getMeta();\n\t\t\tvar custom = point.custom || {};\n\t\t\tvar xScale = me.getScaleForId(meta.xAxisID);\n\t\t\tvar yScale = me.getScaleForId(meta.yAxisID);\n\t\t\tvar options = me._resolveElementOptions(point, index);\n\t\t\tvar data = me.getDataset().data[index];\n\t\t\tvar dsIndex = me.index;\n\n\t\t\tvar x = reset ? xScale.getPixelForDecimal(0.5) : xScale.getPixelForValue(typeof data === 'object' ? data : NaN, index, dsIndex);\n\t\t\tvar y = reset ? yScale.getBasePixel() : yScale.getPixelForValue(data, index, dsIndex);\n\n\t\t\tpoint._xScale = xScale;\n\t\t\tpoint._yScale = yScale;\n\t\t\tpoint._options = options;\n\t\t\tpoint._datasetIndex = dsIndex;\n\t\t\tpoint._index = index;\n\t\t\tpoint._model = {\n\t\t\t\tbackgroundColor: options.backgroundColor,\n\t\t\t\tborderColor: options.borderColor,\n\t\t\t\tborderWidth: options.borderWidth,\n\t\t\t\thitRadius: options.hitRadius,\n\t\t\t\tpointStyle: options.pointStyle,\n\t\t\t\tradius: reset ? 0 : options.radius,\n\t\t\t\tskip: custom.skip || isNaN(x) || isNaN(y),\n\t\t\t\tx: x,\n\t\t\t\ty: y,\n\t\t\t};\n\n\t\t\tpoint.pivot();\n\t\t},\n\n\t\t/**\n\t\t * @protected\n\t\t */\n\t\tsetHoverStyle: function(point) {\n\t\t\tvar model = point._model;\n\t\t\tvar options = point._options;\n\n\t\t\tmodel.backgroundColor = helpers.valueOrDefault(options.hoverBackgroundColor, helpers.getHoverColor(options.backgroundColor));\n\t\t\tmodel.borderColor = helpers.valueOrDefault(options.hoverBorderColor, helpers.getHoverColor(options.borderColor));\n\t\t\tmodel.borderWidth = helpers.valueOrDefault(options.hoverBorderWidth, options.borderWidth);\n\t\t\tmodel.radius = options.radius + options.hoverRadius;\n\t\t},\n\n\t\t/**\n\t\t * @protected\n\t\t */\n\t\tremoveHoverStyle: function(point) {\n\t\t\tvar model = point._model;\n\t\t\tvar options = point._options;\n\n\t\t\tmodel.backgroundColor = options.backgroundColor;\n\t\t\tmodel.borderColor = options.borderColor;\n\t\t\tmodel.borderWidth = options.borderWidth;\n\t\t\tmodel.radius = options.radius;\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\t_resolveElementOptions: function(point, index) {\n\t\t\tvar me = this;\n\t\t\tvar chart = me.chart;\n\t\t\tvar datasets = chart.data.datasets;\n\t\t\tvar dataset = datasets[me.index];\n\t\t\tvar custom = point.custom || {};\n\t\t\tvar options = chart.options.elements.point;\n\t\t\tvar resolve = helpers.options.resolve;\n\t\t\tvar data = dataset.data[index];\n\t\t\tvar values = {};\n\t\t\tvar i, ilen, key;\n\n\t\t\t// Scriptable options\n\t\t\tvar context = {\n\t\t\t\tchart: chart,\n\t\t\t\tdataIndex: index,\n\t\t\t\tdataset: dataset,\n\t\t\t\tdatasetIndex: me.index\n\t\t\t};\n\n\t\t\tvar keys = [\n\t\t\t\t'backgroundColor',\n\t\t\t\t'borderColor',\n\t\t\t\t'borderWidth',\n\t\t\t\t'hoverBackgroundColor',\n\t\t\t\t'hoverBorderColor',\n\t\t\t\t'hoverBorderWidth',\n\t\t\t\t'hoverRadius',\n\t\t\t\t'hitRadius',\n\t\t\t\t'pointStyle'\n\t\t\t];\n\n\t\t\tfor (i = 0, ilen = keys.length; i < ilen; ++i) {\n\t\t\t\tkey = keys[i];\n\t\t\t\tvalues[key] = resolve([\n\t\t\t\t\tcustom[key],\n\t\t\t\t\tdataset[key],\n\t\t\t\t\toptions[key]\n\t\t\t\t], context, index);\n\t\t\t}\n\n\t\t\t// Custom radius resolution\n\t\t\tvalues.radius = resolve([\n\t\t\t\tcustom.radius,\n\t\t\t\tdata ? data.r : undefined,\n\t\t\t\tdataset.radius,\n\t\t\t\toptions.radius\n\t\t\t], context, index);\n\n\t\t\treturn values;\n\t\t}\n\t});\n};\n\n},{\"25\":25,\"40\":40,\"45\":45}],17:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\nvar elements = require(40);\nvar helpers = require(45);\n\ndefaults._set('doughnut', {\n\tanimation: {\n\t\t// Boolean - Whether we animate the rotation of the Doughnut\n\t\tanimateRotate: true,\n\t\t// Boolean - Whether we animate scaling the Doughnut from the centre\n\t\tanimateScale: false\n\t},\n\thover: {\n\t\tmode: 'single'\n\t},\n\tlegendCallback: function(chart) {\n\t\tvar text = [];\n\t\ttext.push('<ul class=\"' + chart.id + '-legend\">');\n\n\t\tvar data = chart.data;\n\t\tvar datasets = data.datasets;\n\t\tvar labels = data.labels;\n\n\t\tif (datasets.length) {\n\t\t\tfor (var i = 0; i < datasets[0].data.length; ++i) {\n\t\t\t\ttext.push('<li><span style=\"background-color:' + datasets[0].backgroundColor[i] + '\"></span>');\n\t\t\t\tif (labels[i]) {\n\t\t\t\t\ttext.push(labels[i]);\n\t\t\t\t}\n\t\t\t\ttext.push('</li>');\n\t\t\t}\n\t\t}\n\n\t\ttext.push('</ul>');\n\t\treturn text.join('');\n\t},\n\tlegend: {\n\t\tlabels: {\n\t\t\tgenerateLabels: function(chart) {\n\t\t\t\tvar data = chart.data;\n\t\t\t\tif (data.labels.length && data.datasets.length) {\n\t\t\t\t\treturn data.labels.map(function(label, i) {\n\t\t\t\t\t\tvar meta = chart.getDatasetMeta(0);\n\t\t\t\t\t\tvar ds = data.datasets[0];\n\t\t\t\t\t\tvar arc = meta.data[i];\n\t\t\t\t\t\tvar custom = arc && arc.custom || {};\n\t\t\t\t\t\tvar valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;\n\t\t\t\t\t\tvar arcOpts = chart.options.elements.arc;\n\t\t\t\t\t\tvar fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);\n\t\t\t\t\t\tvar stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);\n\t\t\t\t\t\tvar bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);\n\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttext: label,\n\t\t\t\t\t\t\tfillStyle: fill,\n\t\t\t\t\t\t\tstrokeStyle: stroke,\n\t\t\t\t\t\t\tlineWidth: bw,\n\t\t\t\t\t\t\thidden: isNaN(ds.data[i]) || meta.data[i].hidden,\n\n\t\t\t\t\t\t\t// Extra data used for toggling the correct item\n\t\t\t\t\t\t\tindex: i\n\t\t\t\t\t\t};\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn [];\n\t\t\t}\n\t\t},\n\n\t\tonClick: function(e, legendItem) {\n\t\t\tvar index = legendItem.index;\n\t\t\tvar chart = this.chart;\n\t\t\tvar i, ilen, meta;\n\n\t\t\tfor (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {\n\t\t\t\tmeta = chart.getDatasetMeta(i);\n\t\t\t\t// toggle visibility of index if exists\n\t\t\t\tif (meta.data[index]) {\n\t\t\t\t\tmeta.data[index].hidden = !meta.data[index].hidden;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tchart.update();\n\t\t}\n\t},\n\n\t// The percentage of the chart that we cut out of the middle.\n\tcutoutPercentage: 50,\n\n\t// The rotation of the chart, where the first data arc begins.\n\trotation: Math.PI * -0.5,\n\n\t// The total circumference of the chart.\n\tcircumference: Math.PI * 2.0,\n\n\t// Need to override these to give a nice default\n\ttooltips: {\n\t\tcallbacks: {\n\t\t\ttitle: function() {\n\t\t\t\treturn '';\n\t\t\t},\n\t\t\tlabel: function(tooltipItem, data) {\n\t\t\t\tvar dataLabel = data.labels[tooltipItem.index];\n\t\t\t\tvar value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];\n\n\t\t\t\tif (helpers.isArray(dataLabel)) {\n\t\t\t\t\t// show value on first line of multiline label\n\t\t\t\t\t// need to clone because we are changing the value\n\t\t\t\t\tdataLabel = dataLabel.slice();\n\t\t\t\t\tdataLabel[0] += value;\n\t\t\t\t} else {\n\t\t\t\t\tdataLabel += value;\n\t\t\t\t}\n\n\t\t\t\treturn dataLabel;\n\t\t\t}\n\t\t}\n\t}\n});\n\ndefaults._set('pie', helpers.clone(defaults.doughnut));\ndefaults._set('pie', {\n\tcutoutPercentage: 0\n});\n\nmodule.exports = function(Chart) {\n\n\tChart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({\n\n\t\tdataElementType: elements.Arc,\n\n\t\tlinkScales: helpers.noop,\n\n\t\t// Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly\n\t\tgetRingIndex: function(datasetIndex) {\n\t\t\tvar ringIndex = 0;\n\n\t\t\tfor (var j = 0; j < datasetIndex; ++j) {\n\t\t\t\tif (this.chart.isDatasetVisible(j)) {\n\t\t\t\t\t++ringIndex;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn ringIndex;\n\t\t},\n\n\t\tupdate: function(reset) {\n\t\t\tvar me = this;\n\t\t\tvar chart = me.chart;\n\t\t\tvar chartArea = chart.chartArea;\n\t\t\tvar opts = chart.options;\n\t\t\tvar arcOpts = opts.elements.arc;\n\t\t\tvar availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth;\n\t\t\tvar availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth;\n\t\t\tvar minSize = Math.min(availableWidth, availableHeight);\n\t\t\tvar offset = {x: 0, y: 0};\n\t\t\tvar meta = me.getMeta();\n\t\t\tvar cutoutPercentage = opts.cutoutPercentage;\n\t\t\tvar circumference = opts.circumference;\n\n\t\t\t// If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc\n\t\t\tif (circumference < Math.PI * 2.0) {\n\t\t\t\tvar startAngle = opts.rotation % (Math.PI * 2.0);\n\t\t\t\tstartAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);\n\t\t\t\tvar endAngle = startAngle + circumference;\n\t\t\t\tvar start = {x: Math.cos(startAngle), y: Math.sin(startAngle)};\n\t\t\t\tvar end = {x: Math.cos(endAngle), y: Math.sin(endAngle)};\n\t\t\t\tvar contains0 = (startAngle <= 0 && endAngle >= 0) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle);\n\t\t\t\tvar contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle);\n\t\t\t\tvar contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle);\n\t\t\t\tvar contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle);\n\t\t\t\tvar cutout = cutoutPercentage / 100.0;\n\t\t\t\tvar min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))};\n\t\t\t\tvar max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))};\n\t\t\t\tvar size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5};\n\t\t\t\tminSize = Math.min(availableWidth / size.width, availableHeight / size.height);\n\t\t\t\toffset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};\n\t\t\t}\n\n\t\t\tchart.borderWidth = me.getMaxBorderWidth(meta.data);\n\t\t\tchart.outerRadius = Math.max((minSize - chart.borderWidth) / 2, 0);\n\t\t\tchart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 0, 0);\n\t\t\tchart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();\n\t\t\tchart.offsetX = offset.x * chart.outerRadius;\n\t\t\tchart.offsetY = offset.y * chart.outerRadius;\n\n\t\t\tmeta.total = me.calculateTotal();\n\n\t\t\tme.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index));\n\t\t\tme.innerRadius = Math.max(me.outerRadius - chart.radiusLength, 0);\n\n\t\t\thelpers.each(meta.data, function(arc, index) {\n\t\t\t\tme.updateElement(arc, index, reset);\n\t\t\t});\n\t\t},\n\n\t\tupdateElement: function(arc, index, reset) {\n\t\t\tvar me = this;\n\t\t\tvar chart = me.chart;\n\t\t\tvar chartArea = chart.chartArea;\n\t\t\tvar opts = chart.options;\n\t\t\tvar animationOpts = opts.animation;\n\t\t\tvar centerX = (chartArea.left + chartArea.right) / 2;\n\t\t\tvar centerY = (chartArea.top + chartArea.bottom) / 2;\n\t\t\tvar startAngle = opts.rotation; // non reset case handled later\n\t\t\tvar endAngle = opts.rotation; // non reset case handled later\n\t\t\tvar dataset = me.getDataset();\n\t\t\tvar circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI));\n\t\t\tvar innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius;\n\t\t\tvar outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius;\n\t\t\tvar valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;\n\n\t\t\thelpers.extend(arc, {\n\t\t\t\t// Utility\n\t\t\t\t_datasetIndex: me.index,\n\t\t\t\t_index: index,\n\n\t\t\t\t// Desired view properties\n\t\t\t\t_model: {\n\t\t\t\t\tx: centerX + chart.offsetX,\n\t\t\t\t\ty: centerY + chart.offsetY,\n\t\t\t\t\tstartAngle: startAngle,\n\t\t\t\t\tendAngle: endAngle,\n\t\t\t\t\tcircumference: circumference,\n\t\t\t\t\touterRadius: outerRadius,\n\t\t\t\t\tinnerRadius: innerRadius,\n\t\t\t\t\tlabel: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tvar model = arc._model;\n\t\t\t// Resets the visual styles\n\t\t\tthis.removeHoverStyle(arc);\n\n\t\t\t// Set correct angles if not resetting\n\t\t\tif (!reset || !animationOpts.animateRotate) {\n\t\t\t\tif (index === 0) {\n\t\t\t\t\tmodel.startAngle = opts.rotation;\n\t\t\t\t} else {\n\t\t\t\t\tmodel.startAngle = me.getMeta().data[index - 1]._model.endAngle;\n\t\t\t\t}\n\n\t\t\t\tmodel.endAngle = model.startAngle + model.circumference;\n\t\t\t}\n\n\t\t\tarc.pivot();\n\t\t},\n\n\t\tremoveHoverStyle: function(arc) {\n\t\t\tChart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc);\n\t\t},\n\n\t\tcalculateTotal: function() {\n\t\t\tvar dataset = this.getDataset();\n\t\t\tvar meta = this.getMeta();\n\t\t\tvar total = 0;\n\t\t\tvar value;\n\n\t\t\thelpers.each(meta.data, function(element, index) {\n\t\t\t\tvalue = dataset.data[index];\n\t\t\t\tif (!isNaN(value) && !element.hidden) {\n\t\t\t\t\ttotal += Math.abs(value);\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t/* if (total === 0) {\n\t\t\t\ttotal = NaN;\n\t\t\t}*/\n\n\t\t\treturn total;\n\t\t},\n\n\t\tcalculateCircumference: function(value) {\n\t\t\tvar total = this.getMeta().total;\n\t\t\tif (total > 0 && !isNaN(value)) {\n\t\t\t\treturn (Math.PI * 2.0) * (Math.abs(value) / total);\n\t\t\t}\n\t\t\treturn 0;\n\t\t},\n\n\t\t// gets the max border or hover width to properly scale pie charts\n\t\tgetMaxBorderWidth: function(arcs) {\n\t\t\tvar max = 0;\n\t\t\tvar index = this.index;\n\t\t\tvar length = arcs.length;\n\t\t\tvar borderWidth;\n\t\t\tvar hoverWidth;\n\n\t\t\tfor (var i = 0; i < length; i++) {\n\t\t\t\tborderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0;\n\t\t\t\thoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0;\n\n\t\t\t\tmax = borderWidth > max ? borderWidth : max;\n\t\t\t\tmax = hoverWidth > max ? hoverWidth : max;\n\t\t\t}\n\t\t\treturn max;\n\t\t}\n\t});\n};\n\n},{\"25\":25,\"40\":40,\"45\":45}],18:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\nvar elements = require(40);\nvar helpers = require(45);\n\ndefaults._set('line', {\n\tshowLines: true,\n\tspanGaps: false,\n\n\thover: {\n\t\tmode: 'label'\n\t},\n\n\tscales: {\n\t\txAxes: [{\n\t\t\ttype: 'category',\n\t\t\tid: 'x-axis-0'\n\t\t}],\n\t\tyAxes: [{\n\t\t\ttype: 'linear',\n\t\t\tid: 'y-axis-0'\n\t\t}]\n\t}\n});\n\nmodule.exports = function(Chart) {\n\n\tfunction lineEnabled(dataset, options) {\n\t\treturn helpers.valueOrDefault(dataset.showLine, options.showLines);\n\t}\n\n\tChart.controllers.line = Chart.DatasetController.extend({\n\n\t\tdatasetElementType: elements.Line,\n\n\t\tdataElementType: elements.Point,\n\n\t\tupdate: function(reset) {\n\t\t\tvar me = this;\n\t\t\tvar meta = me.getMeta();\n\t\t\tvar line = meta.dataset;\n\t\t\tvar points = meta.data || [];\n\t\t\tvar options = me.chart.options;\n\t\t\tvar lineElementOptions = options.elements.line;\n\t\t\tvar scale = me.getScaleForId(meta.yAxisID);\n\t\t\tvar i, ilen, custom;\n\t\t\tvar dataset = me.getDataset();\n\t\t\tvar showLine = lineEnabled(dataset, options);\n\n\t\t\t// Update Line\n\t\t\tif (showLine) {\n\t\t\t\tcustom = line.custom || {};\n\n\t\t\t\t// Compatibility: If the properties are defined with only the old name, use those values\n\t\t\t\tif ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {\n\t\t\t\t\tdataset.lineTension = dataset.tension;\n\t\t\t\t}\n\n\t\t\t\t// Utility\n\t\t\t\tline._scale = scale;\n\t\t\t\tline._datasetIndex = me.index;\n\t\t\t\t// Data\n\t\t\t\tline._children = points;\n\t\t\t\t// Model\n\t\t\t\tline._model = {\n\t\t\t\t\t// Appearance\n\t\t\t\t\t// The default behavior of lines is to break at null values, according\n\t\t\t\t\t// to https://github.com/chartjs/Chart.js/issues/2435#issuecomment-216718158\n\t\t\t\t\t// This option gives lines the ability to span gaps\n\t\t\t\t\tspanGaps: dataset.spanGaps ? dataset.spanGaps : options.spanGaps,\n\t\t\t\t\ttension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension),\n\t\t\t\t\tbackgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),\n\t\t\t\t\tborderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),\n\t\t\t\t\tborderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),\n\t\t\t\t\tborderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),\n\t\t\t\t\tborderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),\n\t\t\t\t\tborderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),\n\t\t\t\t\tborderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),\n\t\t\t\t\tfill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),\n\t\t\t\t\tsteppedLine: custom.steppedLine ? custom.steppedLine : helpers.valueOrDefault(dataset.steppedLine, lineElementOptions.stepped),\n\t\t\t\t\tcubicInterpolationMode: custom.cubicInterpolationMode ? custom.cubicInterpolationMode : helpers.valueOrDefault(dataset.cubicInterpolationMode, lineElementOptions.cubicInterpolationMode),\n\t\t\t\t};\n\n\t\t\t\tline.pivot();\n\t\t\t}\n\n\t\t\t// Update Points\n\t\t\tfor (i = 0, ilen = points.length; i < ilen; ++i) {\n\t\t\t\tme.updateElement(points[i], i, reset);\n\t\t\t}\n\n\t\t\tif (showLine && line._model.tension !== 0) {\n\t\t\t\tme.updateBezierControlPoints();\n\t\t\t}\n\n\t\t\t// Now pivot the point for animation\n\t\t\tfor (i = 0, ilen = points.length; i < ilen; ++i) {\n\t\t\t\tpoints[i].pivot();\n\t\t\t}\n\t\t},\n\n\t\tgetPointBackgroundColor: function(point, index) {\n\t\t\tvar backgroundColor = this.chart.options.elements.point.backgroundColor;\n\t\t\tvar dataset = this.getDataset();\n\t\t\tvar custom = point.custom || {};\n\n\t\t\tif (custom.backgroundColor) {\n\t\t\t\tbackgroundColor = custom.backgroundColor;\n\t\t\t} else if (dataset.pointBackgroundColor) {\n\t\t\t\tbackgroundColor = helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, backgroundColor);\n\t\t\t} else if (dataset.backgroundColor) {\n\t\t\t\tbackgroundColor = dataset.backgroundColor;\n\t\t\t}\n\n\t\t\treturn backgroundColor;\n\t\t},\n\n\t\tgetPointBorderColor: function(point, index) {\n\t\t\tvar borderColor = this.chart.options.elements.point.borderColor;\n\t\t\tvar dataset = this.getDataset();\n\t\t\tvar custom = point.custom || {};\n\n\t\t\tif (custom.borderColor) {\n\t\t\t\tborderColor = custom.borderColor;\n\t\t\t} else if (dataset.pointBorderColor) {\n\t\t\t\tborderColor = helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, borderColor);\n\t\t\t} else if (dataset.borderColor) {\n\t\t\t\tborderColor = dataset.borderColor;\n\t\t\t}\n\n\t\t\treturn borderColor;\n\t\t},\n\n\t\tgetPointBorderWidth: function(point, index) {\n\t\t\tvar borderWidth = this.chart.options.elements.point.borderWidth;\n\t\t\tvar dataset = this.getDataset();\n\t\t\tvar custom = point.custom || {};\n\n\t\t\tif (!isNaN(custom.borderWidth)) {\n\t\t\t\tborderWidth = custom.borderWidth;\n\t\t\t} else if (!isNaN(dataset.pointBorderWidth) || helpers.isArray(dataset.pointBorderWidth)) {\n\t\t\t\tborderWidth = helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, borderWidth);\n\t\t\t} else if (!isNaN(dataset.borderWidth)) {\n\t\t\t\tborderWidth = dataset.borderWidth;\n\t\t\t}\n\n\t\t\treturn borderWidth;\n\t\t},\n\n\t\tupdateElement: function(point, index, reset) {\n\t\t\tvar me = this;\n\t\t\tvar meta = me.getMeta();\n\t\t\tvar custom = point.custom || {};\n\t\t\tvar dataset = me.getDataset();\n\t\t\tvar datasetIndex = me.index;\n\t\t\tvar value = dataset.data[index];\n\t\t\tvar yScale = me.getScaleForId(meta.yAxisID);\n\t\t\tvar xScale = me.getScaleForId(meta.xAxisID);\n\t\t\tvar pointOptions = me.chart.options.elements.point;\n\t\t\tvar x, y;\n\n\t\t\t// Compatibility: If the properties are defined with only the old name, use those values\n\t\t\tif ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {\n\t\t\t\tdataset.pointRadius = dataset.radius;\n\t\t\t}\n\t\t\tif ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {\n\t\t\t\tdataset.pointHitRadius = dataset.hitRadius;\n\t\t\t}\n\n\t\t\tx = xScale.getPixelForValue(typeof value === 'object' ? value : NaN, index, datasetIndex);\n\t\t\ty = reset ? yScale.getBasePixel() : me.calculatePointY(value, index, datasetIndex);\n\n\t\t\t// Utility\n\t\t\tpoint._xScale = xScale;\n\t\t\tpoint._yScale = yScale;\n\t\t\tpoint._datasetIndex = datasetIndex;\n\t\t\tpoint._index = index;\n\n\t\t\t// Desired view properties\n\t\t\tpoint._model = {\n\t\t\t\tx: x,\n\t\t\t\ty: y,\n\t\t\t\tskip: custom.skip || isNaN(x) || isNaN(y),\n\t\t\t\t// Appearance\n\t\t\t\tradius: custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointOptions.radius),\n\t\t\t\tpointStyle: custom.pointStyle || helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointOptions.pointStyle),\n\t\t\t\tbackgroundColor: me.getPointBackgroundColor(point, index),\n\t\t\t\tborderColor: me.getPointBorderColor(point, index),\n\t\t\t\tborderWidth: me.getPointBorderWidth(point, index),\n\t\t\t\ttension: meta.dataset._model ? meta.dataset._model.tension : 0,\n\t\t\t\tsteppedLine: meta.dataset._model ? meta.dataset._model.steppedLine : false,\n\t\t\t\t// Tooltip\n\t\t\t\thitRadius: custom.hitRadius || helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointOptions.hitRadius)\n\t\t\t};\n\t\t},\n\n\t\tcalculatePointY: function(value, index, datasetIndex) {\n\t\t\tvar me = this;\n\t\t\tvar chart = me.chart;\n\t\t\tvar meta = me.getMeta();\n\t\t\tvar yScale = me.getScaleForId(meta.yAxisID);\n\t\t\tvar sumPos = 0;\n\t\t\tvar sumNeg = 0;\n\t\t\tvar i, ds, dsMeta;\n\n\t\t\tif (yScale.options.stacked) {\n\t\t\t\tfor (i = 0; i < datasetIndex; i++) {\n\t\t\t\t\tds = chart.data.datasets[i];\n\t\t\t\t\tdsMeta = chart.getDatasetMeta(i);\n\t\t\t\t\tif (dsMeta.type === 'line' && dsMeta.yAxisID === yScale.id && chart.isDatasetVisible(i)) {\n\t\t\t\t\t\tvar stackedRightValue = Number(yScale.getRightValue(ds.data[index]));\n\t\t\t\t\t\tif (stackedRightValue < 0) {\n\t\t\t\t\t\t\tsumNeg += stackedRightValue || 0;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tsumPos += stackedRightValue || 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tvar rightValue = Number(yScale.getRightValue(value));\n\t\t\t\tif (rightValue < 0) {\n\t\t\t\t\treturn yScale.getPixelForValue(sumNeg + rightValue);\n\t\t\t\t}\n\t\t\t\treturn yScale.getPixelForValue(sumPos + rightValue);\n\t\t\t}\n\n\t\t\treturn yScale.getPixelForValue(value);\n\t\t},\n\n\t\tupdateBezierControlPoints: function() {\n\t\t\tvar me = this;\n\t\t\tvar meta = me.getMeta();\n\t\t\tvar area = me.chart.chartArea;\n\t\t\tvar points = (meta.data || []);\n\t\t\tvar i, ilen, point, model, controlPoints;\n\n\t\t\t// Only consider points that are drawn in case the spanGaps option is used\n\t\t\tif (meta.dataset._model.spanGaps) {\n\t\t\t\tpoints = points.filter(function(pt) {\n\t\t\t\t\treturn !pt._model.skip;\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tfunction capControlPoint(pt, min, max) {\n\t\t\t\treturn Math.max(Math.min(pt, max), min);\n\t\t\t}\n\n\t\t\tif (meta.dataset._model.cubicInterpolationMode === 'monotone') {\n\t\t\t\thelpers.splineCurveMonotone(points);\n\t\t\t} else {\n\t\t\t\tfor (i = 0, ilen = points.length; i < ilen; ++i) {\n\t\t\t\t\tpoint = points[i];\n\t\t\t\t\tmodel = point._model;\n\t\t\t\t\tcontrolPoints = helpers.splineCurve(\n\t\t\t\t\t\thelpers.previousItem(points, i)._model,\n\t\t\t\t\t\tmodel,\n\t\t\t\t\t\thelpers.nextItem(points, i)._model,\n\t\t\t\t\t\tmeta.dataset._model.tension\n\t\t\t\t\t);\n\t\t\t\t\tmodel.controlPointPreviousX = controlPoints.previous.x;\n\t\t\t\t\tmodel.controlPointPreviousY = controlPoints.previous.y;\n\t\t\t\t\tmodel.controlPointNextX = controlPoints.next.x;\n\t\t\t\t\tmodel.controlPointNextY = controlPoints.next.y;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (me.chart.options.elements.line.capBezierPoints) {\n\t\t\t\tfor (i = 0, ilen = points.length; i < ilen; ++i) {\n\t\t\t\t\tmodel = points[i]._model;\n\t\t\t\t\tmodel.controlPointPreviousX = capControlPoint(model.controlPointPreviousX, area.left, area.right);\n\t\t\t\t\tmodel.controlPointPreviousY = capControlPoint(model.controlPointPreviousY, area.top, area.bottom);\n\t\t\t\t\tmodel.controlPointNextX = capControlPoint(model.controlPointNextX, area.left, area.right);\n\t\t\t\t\tmodel.controlPointNextY = capControlPoint(model.controlPointNextY, area.top, area.bottom);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\tdraw: function() {\n\t\t\tvar me = this;\n\t\t\tvar chart = me.chart;\n\t\t\tvar meta = me.getMeta();\n\t\t\tvar points = meta.data || [];\n\t\t\tvar area = chart.chartArea;\n\t\t\tvar ilen = points.length;\n\t\t\tvar i = 0;\n\n\t\t\thelpers.canvas.clipArea(chart.ctx, area);\n\n\t\t\tif (lineEnabled(me.getDataset(), chart.options)) {\n\t\t\t\tmeta.dataset.draw();\n\t\t\t}\n\n\t\t\thelpers.canvas.unclipArea(chart.ctx);\n\n\t\t\t// Draw the points\n\t\t\tfor (; i < ilen; ++i) {\n\t\t\t\tpoints[i].draw(area);\n\t\t\t}\n\t\t},\n\n\t\tsetHoverStyle: function(point) {\n\t\t\t// Point\n\t\t\tvar dataset = this.chart.data.datasets[point._datasetIndex];\n\t\t\tvar index = point._index;\n\t\t\tvar custom = point.custom || {};\n\t\t\tvar model = point._model;\n\n\t\t\tmodel.radius = custom.hoverRadius || helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);\n\t\t\tmodel.backgroundColor = custom.hoverBackgroundColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));\n\t\t\tmodel.borderColor = custom.hoverBorderColor || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));\n\t\t\tmodel.borderWidth = custom.hoverBorderWidth || helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);\n\t\t},\n\n\t\tremoveHoverStyle: function(point) {\n\t\t\tvar me = this;\n\t\t\tvar dataset = me.chart.data.datasets[point._datasetIndex];\n\t\t\tvar index = point._index;\n\t\t\tvar custom = point.custom || {};\n\t\t\tvar model = point._model;\n\n\t\t\t// Compatibility: If the properties are defined with only the old name, use those values\n\t\t\tif ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {\n\t\t\t\tdataset.pointRadius = dataset.radius;\n\t\t\t}\n\n\t\t\tmodel.radius = custom.radius || helpers.valueAtIndexOrDefault(dataset.pointRadius, index, me.chart.options.elements.point.radius);\n\t\t\tmodel.backgroundColor = me.getPointBackgroundColor(point, index);\n\t\t\tmodel.borderColor = me.getPointBorderColor(point, index);\n\t\t\tmodel.borderWidth = me.getPointBorderWidth(point, index);\n\t\t}\n\t});\n};\n\n},{\"25\":25,\"40\":40,\"45\":45}],19:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\nvar elements = require(40);\nvar helpers = require(45);\n\ndefaults._set('polarArea', {\n\tscale: {\n\t\ttype: 'radialLinear',\n\t\tangleLines: {\n\t\t\tdisplay: false\n\t\t},\n\t\tgridLines: {\n\t\t\tcircular: true\n\t\t},\n\t\tpointLabels: {\n\t\t\tdisplay: false\n\t\t},\n\t\tticks: {\n\t\t\tbeginAtZero: true\n\t\t}\n\t},\n\n\t// Boolean - Whether to animate the rotation of the chart\n\tanimation: {\n\t\tanimateRotate: true,\n\t\tanimateScale: true\n\t},\n\n\tstartAngle: -0.5 * Math.PI,\n\tlegendCallback: function(chart) {\n\t\tvar text = [];\n\t\ttext.push('<ul class=\"' + chart.id + '-legend\">');\n\n\t\tvar data = chart.data;\n\t\tvar datasets = data.datasets;\n\t\tvar labels = data.labels;\n\n\t\tif (datasets.length) {\n\t\t\tfor (var i = 0; i < datasets[0].data.length; ++i) {\n\t\t\t\ttext.push('<li><span style=\"background-color:' + datasets[0].backgroundColor[i] + '\"></span>');\n\t\t\t\tif (labels[i]) {\n\t\t\t\t\ttext.push(labels[i]);\n\t\t\t\t}\n\t\t\t\ttext.push('</li>');\n\t\t\t}\n\t\t}\n\n\t\ttext.push('</ul>');\n\t\treturn text.join('');\n\t},\n\tlegend: {\n\t\tlabels: {\n\t\t\tgenerateLabels: function(chart) {\n\t\t\t\tvar data = chart.data;\n\t\t\t\tif (data.labels.length && data.datasets.length) {\n\t\t\t\t\treturn data.labels.map(function(label, i) {\n\t\t\t\t\t\tvar meta = chart.getDatasetMeta(0);\n\t\t\t\t\t\tvar ds = data.datasets[0];\n\t\t\t\t\t\tvar arc = meta.data[i];\n\t\t\t\t\t\tvar custom = arc.custom || {};\n\t\t\t\t\t\tvar valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;\n\t\t\t\t\t\tvar arcOpts = chart.options.elements.arc;\n\t\t\t\t\t\tvar fill = custom.backgroundColor ? custom.backgroundColor : valueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);\n\t\t\t\t\t\tvar stroke = custom.borderColor ? custom.borderColor : valueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);\n\t\t\t\t\t\tvar bw = custom.borderWidth ? custom.borderWidth : valueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);\n\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttext: label,\n\t\t\t\t\t\t\tfillStyle: fill,\n\t\t\t\t\t\t\tstrokeStyle: stroke,\n\t\t\t\t\t\t\tlineWidth: bw,\n\t\t\t\t\t\t\thidden: isNaN(ds.data[i]) || meta.data[i].hidden,\n\n\t\t\t\t\t\t\t// Extra data used for toggling the correct item\n\t\t\t\t\t\t\tindex: i\n\t\t\t\t\t\t};\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn [];\n\t\t\t}\n\t\t},\n\n\t\tonClick: function(e, legendItem) {\n\t\t\tvar index = legendItem.index;\n\t\t\tvar chart = this.chart;\n\t\t\tvar i, ilen, meta;\n\n\t\t\tfor (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {\n\t\t\t\tmeta = chart.getDatasetMeta(i);\n\t\t\t\tmeta.data[index].hidden = !meta.data[index].hidden;\n\t\t\t}\n\n\t\t\tchart.update();\n\t\t}\n\t},\n\n\t// Need to override these to give a nice default\n\ttooltips: {\n\t\tcallbacks: {\n\t\t\ttitle: function() {\n\t\t\t\treturn '';\n\t\t\t},\n\t\t\tlabel: function(item, data) {\n\t\t\t\treturn data.labels[item.index] + ': ' + item.yLabel;\n\t\t\t}\n\t\t}\n\t}\n});\n\nmodule.exports = function(Chart) {\n\n\tChart.controllers.polarArea = Chart.DatasetController.extend({\n\n\t\tdataElementType: elements.Arc,\n\n\t\tlinkScales: helpers.noop,\n\n\t\tupdate: function(reset) {\n\t\t\tvar me = this;\n\t\t\tvar chart = me.chart;\n\t\t\tvar chartArea = chart.chartArea;\n\t\t\tvar meta = me.getMeta();\n\t\t\tvar opts = chart.options;\n\t\t\tvar arcOpts = opts.elements.arc;\n\t\t\tvar minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);\n\t\t\tchart.outerRadius = Math.max((minSize - arcOpts.borderWidth / 2) / 2, 0);\n\t\t\tchart.innerRadius = Math.max(opts.cutoutPercentage ? (chart.outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);\n\t\t\tchart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();\n\n\t\t\tme.outerRadius = chart.outerRadius - (chart.radiusLength * me.index);\n\t\t\tme.innerRadius = me.outerRadius - chart.radiusLength;\n\n\t\t\tmeta.count = me.countVisibleElements();\n\n\t\t\thelpers.each(meta.data, function(arc, index) {\n\t\t\t\tme.updateElement(arc, index, reset);\n\t\t\t});\n\t\t},\n\n\t\tupdateElement: function(arc, index, reset) {\n\t\t\tvar me = this;\n\t\t\tvar chart = me.chart;\n\t\t\tvar dataset = me.getDataset();\n\t\t\tvar opts = chart.options;\n\t\t\tvar animationOpts = opts.animation;\n\t\t\tvar scale = chart.scale;\n\t\t\tvar labels = chart.data.labels;\n\n\t\t\tvar circumference = me.calculateCircumference(dataset.data[index]);\n\t\t\tvar centerX = scale.xCenter;\n\t\t\tvar centerY = scale.yCenter;\n\n\t\t\t// If there is NaN data before us, we need to calculate the starting angle correctly.\n\t\t\t// We could be way more efficient here, but its unlikely that the polar area chart will have a lot of data\n\t\t\tvar visibleCount = 0;\n\t\t\tvar meta = me.getMeta();\n\t\t\tfor (var i = 0; i < index; ++i) {\n\t\t\t\tif (!isNaN(dataset.data[i]) && !meta.data[i].hidden) {\n\t\t\t\t\t++visibleCount;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// var negHalfPI = -0.5 * Math.PI;\n\t\t\tvar datasetStartAngle = opts.startAngle;\n\t\t\tvar distance = arc.hidden ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);\n\t\t\tvar startAngle = datasetStartAngle + (circumference * visibleCount);\n\t\t\tvar endAngle = startAngle + (arc.hidden ? 0 : circumference);\n\n\t\t\tvar resetRadius = animationOpts.animateScale ? 0 : scale.getDistanceFromCenterForValue(dataset.data[index]);\n\n\t\t\thelpers.extend(arc, {\n\t\t\t\t// Utility\n\t\t\t\t_datasetIndex: me.index,\n\t\t\t\t_index: index,\n\t\t\t\t_scale: scale,\n\n\t\t\t\t// Desired view properties\n\t\t\t\t_model: {\n\t\t\t\t\tx: centerX,\n\t\t\t\t\ty: centerY,\n\t\t\t\t\tinnerRadius: 0,\n\t\t\t\t\touterRadius: reset ? resetRadius : distance,\n\t\t\t\t\tstartAngle: reset && animationOpts.animateRotate ? datasetStartAngle : startAngle,\n\t\t\t\t\tendAngle: reset && animationOpts.animateRotate ? datasetStartAngle : endAngle,\n\t\t\t\t\tlabel: helpers.valueAtIndexOrDefault(labels, index, labels[index])\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// Apply border and fill style\n\t\t\tme.removeHoverStyle(arc);\n\n\t\t\tarc.pivot();\n\t\t},\n\n\t\tremoveHoverStyle: function(arc) {\n\t\t\tChart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc);\n\t\t},\n\n\t\tcountVisibleElements: function() {\n\t\t\tvar dataset = this.getDataset();\n\t\t\tvar meta = this.getMeta();\n\t\t\tvar count = 0;\n\n\t\t\thelpers.each(meta.data, function(element, index) {\n\t\t\t\tif (!isNaN(dataset.data[index]) && !element.hidden) {\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn count;\n\t\t},\n\n\t\tcalculateCircumference: function(value) {\n\t\t\tvar count = this.getMeta().count;\n\t\t\tif (count > 0 && !isNaN(value)) {\n\t\t\t\treturn (2 * Math.PI) / count;\n\t\t\t}\n\t\t\treturn 0;\n\t\t}\n\t});\n};\n\n},{\"25\":25,\"40\":40,\"45\":45}],20:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\nvar elements = require(40);\nvar helpers = require(45);\n\ndefaults._set('radar', {\n\tscale: {\n\t\ttype: 'radialLinear'\n\t},\n\telements: {\n\t\tline: {\n\t\t\ttension: 0 // no bezier in radar\n\t\t}\n\t}\n});\n\nmodule.exports = function(Chart) {\n\n\tChart.controllers.radar = Chart.DatasetController.extend({\n\n\t\tdatasetElementType: elements.Line,\n\n\t\tdataElementType: elements.Point,\n\n\t\tlinkScales: helpers.noop,\n\n\t\tupdate: function(reset) {\n\t\t\tvar me = this;\n\t\t\tvar meta = me.getMeta();\n\t\t\tvar line = meta.dataset;\n\t\t\tvar points = meta.data;\n\t\t\tvar custom = line.custom || {};\n\t\t\tvar dataset = me.getDataset();\n\t\t\tvar lineElementOptions = me.chart.options.elements.line;\n\t\t\tvar scale = me.chart.scale;\n\n\t\t\t// Compatibility: If the properties are defined with only the old name, use those values\n\t\t\tif ((dataset.tension !== undefined) && (dataset.lineTension === undefined)) {\n\t\t\t\tdataset.lineTension = dataset.tension;\n\t\t\t}\n\n\t\t\thelpers.extend(meta.dataset, {\n\t\t\t\t// Utility\n\t\t\t\t_datasetIndex: me.index,\n\t\t\t\t_scale: scale,\n\t\t\t\t// Data\n\t\t\t\t_children: points,\n\t\t\t\t_loop: true,\n\t\t\t\t// Model\n\t\t\t\t_model: {\n\t\t\t\t\t// Appearance\n\t\t\t\t\ttension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, lineElementOptions.tension),\n\t\t\t\t\tbackgroundColor: custom.backgroundColor ? custom.backgroundColor : (dataset.backgroundColor || lineElementOptions.backgroundColor),\n\t\t\t\t\tborderWidth: custom.borderWidth ? custom.borderWidth : (dataset.borderWidth || lineElementOptions.borderWidth),\n\t\t\t\t\tborderColor: custom.borderColor ? custom.borderColor : (dataset.borderColor || lineElementOptions.borderColor),\n\t\t\t\t\tfill: custom.fill ? custom.fill : (dataset.fill !== undefined ? dataset.fill : lineElementOptions.fill),\n\t\t\t\t\tborderCapStyle: custom.borderCapStyle ? custom.borderCapStyle : (dataset.borderCapStyle || lineElementOptions.borderCapStyle),\n\t\t\t\t\tborderDash: custom.borderDash ? custom.borderDash : (dataset.borderDash || lineElementOptions.borderDash),\n\t\t\t\t\tborderDashOffset: custom.borderDashOffset ? custom.borderDashOffset : (dataset.borderDashOffset || lineElementOptions.borderDashOffset),\n\t\t\t\t\tborderJoinStyle: custom.borderJoinStyle ? custom.borderJoinStyle : (dataset.borderJoinStyle || lineElementOptions.borderJoinStyle),\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tmeta.dataset.pivot();\n\n\t\t\t// Update Points\n\t\t\thelpers.each(points, function(point, index) {\n\t\t\t\tme.updateElement(point, index, reset);\n\t\t\t}, me);\n\n\t\t\t// Update bezier control points\n\t\t\tme.updateBezierControlPoints();\n\t\t},\n\t\tupdateElement: function(point, index, reset) {\n\t\t\tvar me = this;\n\t\t\tvar custom = point.custom || {};\n\t\t\tvar dataset = me.getDataset();\n\t\t\tvar scale = me.chart.scale;\n\t\t\tvar pointElementOptions = me.chart.options.elements.point;\n\t\t\tvar pointPosition = scale.getPointPositionForValue(index, dataset.data[index]);\n\n\t\t\t// Compatibility: If the properties are defined with only the old name, use those values\n\t\t\tif ((dataset.radius !== undefined) && (dataset.pointRadius === undefined)) {\n\t\t\t\tdataset.pointRadius = dataset.radius;\n\t\t\t}\n\t\t\tif ((dataset.hitRadius !== undefined) && (dataset.pointHitRadius === undefined)) {\n\t\t\t\tdataset.pointHitRadius = dataset.hitRadius;\n\t\t\t}\n\n\t\t\thelpers.extend(point, {\n\t\t\t\t// Utility\n\t\t\t\t_datasetIndex: me.index,\n\t\t\t\t_index: index,\n\t\t\t\t_scale: scale,\n\n\t\t\t\t// Desired view properties\n\t\t\t\t_model: {\n\t\t\t\t\tx: reset ? scale.xCenter : pointPosition.x, // value not used in dataset scale, but we want a consistent API between scales\n\t\t\t\t\ty: reset ? scale.yCenter : pointPosition.y,\n\n\t\t\t\t\t// Appearance\n\t\t\t\t\ttension: custom.tension ? custom.tension : helpers.valueOrDefault(dataset.lineTension, me.chart.options.elements.line.tension),\n\t\t\t\t\tradius: custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius),\n\t\t\t\t\tbackgroundColor: custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor),\n\t\t\t\t\tborderColor: custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor),\n\t\t\t\t\tborderWidth: custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth),\n\t\t\t\t\tpointStyle: custom.pointStyle ? custom.pointStyle : helpers.valueAtIndexOrDefault(dataset.pointStyle, index, pointElementOptions.pointStyle),\n\n\t\t\t\t\t// Tooltip\n\t\t\t\t\thitRadius: custom.hitRadius ? custom.hitRadius : helpers.valueAtIndexOrDefault(dataset.pointHitRadius, index, pointElementOptions.hitRadius)\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tpoint._model.skip = custom.skip ? custom.skip : (isNaN(point._model.x) || isNaN(point._model.y));\n\t\t},\n\t\tupdateBezierControlPoints: function() {\n\t\t\tvar chartArea = this.chart.chartArea;\n\t\t\tvar meta = this.getMeta();\n\n\t\t\thelpers.each(meta.data, function(point, index) {\n\t\t\t\tvar model = point._model;\n\t\t\t\tvar controlPoints = helpers.splineCurve(\n\t\t\t\t\thelpers.previousItem(meta.data, index, true)._model,\n\t\t\t\t\tmodel,\n\t\t\t\t\thelpers.nextItem(meta.data, index, true)._model,\n\t\t\t\t\tmodel.tension\n\t\t\t\t);\n\n\t\t\t\t// Prevent the bezier going outside of the bounds of the graph\n\t\t\t\tmodel.controlPointPreviousX = Math.max(Math.min(controlPoints.previous.x, chartArea.right), chartArea.left);\n\t\t\t\tmodel.controlPointPreviousY = Math.max(Math.min(controlPoints.previous.y, chartArea.bottom), chartArea.top);\n\n\t\t\t\tmodel.controlPointNextX = Math.max(Math.min(controlPoints.next.x, chartArea.right), chartArea.left);\n\t\t\t\tmodel.controlPointNextY = Math.max(Math.min(controlPoints.next.y, chartArea.bottom), chartArea.top);\n\n\t\t\t\t// Now pivot the point for animation\n\t\t\t\tpoint.pivot();\n\t\t\t});\n\t\t},\n\n\t\tsetHoverStyle: function(point) {\n\t\t\t// Point\n\t\t\tvar dataset = this.chart.data.datasets[point._datasetIndex];\n\t\t\tvar custom = point.custom || {};\n\t\t\tvar index = point._index;\n\t\t\tvar model = point._model;\n\n\t\t\tmodel.radius = custom.hoverRadius ? custom.hoverRadius : helpers.valueAtIndexOrDefault(dataset.pointHoverRadius, index, this.chart.options.elements.point.hoverRadius);\n\t\t\tmodel.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBackgroundColor, index, helpers.getHoverColor(model.backgroundColor));\n\t\t\tmodel.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderColor, index, helpers.getHoverColor(model.borderColor));\n\t\t\tmodel.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : helpers.valueAtIndexOrDefault(dataset.pointHoverBorderWidth, index, model.borderWidth);\n\t\t},\n\n\t\tremoveHoverStyle: function(point) {\n\t\t\tvar dataset = this.chart.data.datasets[point._datasetIndex];\n\t\t\tvar custom = point.custom || {};\n\t\t\tvar index = point._index;\n\t\t\tvar model = point._model;\n\t\t\tvar pointElementOptions = this.chart.options.elements.point;\n\n\t\t\tmodel.radius = custom.radius ? custom.radius : helpers.valueAtIndexOrDefault(dataset.pointRadius, index, pointElementOptions.radius);\n\t\t\tmodel.backgroundColor = custom.backgroundColor ? custom.backgroundColor : helpers.valueAtIndexOrDefault(dataset.pointBackgroundColor, index, pointElementOptions.backgroundColor);\n\t\t\tmodel.borderColor = custom.borderColor ? custom.borderColor : helpers.valueAtIndexOrDefault(dataset.pointBorderColor, index, pointElementOptions.borderColor);\n\t\t\tmodel.borderWidth = custom.borderWidth ? custom.borderWidth : helpers.valueAtIndexOrDefault(dataset.pointBorderWidth, index, pointElementOptions.borderWidth);\n\t\t}\n\t});\n};\n\n},{\"25\":25,\"40\":40,\"45\":45}],21:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\n\ndefaults._set('scatter', {\n\thover: {\n\t\tmode: 'single'\n\t},\n\n\tscales: {\n\t\txAxes: [{\n\t\t\tid: 'x-axis-1',    // need an ID so datasets can reference the scale\n\t\t\ttype: 'linear',    // scatter should not use a category axis\n\t\t\tposition: 'bottom'\n\t\t}],\n\t\tyAxes: [{\n\t\t\tid: 'y-axis-1',\n\t\t\ttype: 'linear',\n\t\t\tposition: 'left'\n\t\t}]\n\t},\n\n\tshowLines: false,\n\n\ttooltips: {\n\t\tcallbacks: {\n\t\t\ttitle: function() {\n\t\t\t\treturn '';     // doesn't make sense for scatter since data are formatted as a point\n\t\t\t},\n\t\t\tlabel: function(item) {\n\t\t\t\treturn '(' + item.xLabel + ', ' + item.yLabel + ')';\n\t\t\t}\n\t\t}\n\t}\n});\n\nmodule.exports = function(Chart) {\n\n\t// Scatter charts use line controllers\n\tChart.controllers.scatter = Chart.controllers.line;\n\n};\n\n},{\"25\":25}],22:[function(require,module,exports){\n/* global window: false */\n'use strict';\n\nvar defaults = require(25);\nvar Element = require(26);\nvar helpers = require(45);\n\ndefaults._set('global', {\n\tanimation: {\n\t\tduration: 1000,\n\t\teasing: 'easeOutQuart',\n\t\tonProgress: helpers.noop,\n\t\tonComplete: helpers.noop\n\t}\n});\n\nmodule.exports = function(Chart) {\n\n\tChart.Animation = Element.extend({\n\t\tchart: null, // the animation associated chart instance\n\t\tcurrentStep: 0, // the current animation step\n\t\tnumSteps: 60, // default number of steps\n\t\teasing: '', // the easing to use for this animation\n\t\trender: null, // render function used by the animation service\n\n\t\tonAnimationProgress: null, // user specified callback to fire on each step of the animation\n\t\tonAnimationComplete: null, // user specified callback to fire when the animation finishes\n\t});\n\n\tChart.animationService = {\n\t\tframeDuration: 17,\n\t\tanimations: [],\n\t\tdropFrames: 0,\n\t\trequest: null,\n\n\t\t/**\n\t\t * @param {Chart} chart - The chart to animate.\n\t\t * @param {Chart.Animation} animation - The animation that we will animate.\n\t\t * @param {Number} duration - The animation duration in ms.\n\t\t * @param {Boolean} lazy - if true, the chart is not marked as animating to enable more responsive interactions\n\t\t */\n\t\taddAnimation: function(chart, animation, duration, lazy) {\n\t\t\tvar animations = this.animations;\n\t\t\tvar i, ilen;\n\n\t\t\tanimation.chart = chart;\n\n\t\t\tif (!lazy) {\n\t\t\t\tchart.animating = true;\n\t\t\t}\n\n\t\t\tfor (i = 0, ilen = animations.length; i < ilen; ++i) {\n\t\t\t\tif (animations[i].chart === chart) {\n\t\t\t\t\tanimations[i] = animation;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tanimations.push(animation);\n\n\t\t\t// If there are no animations queued, manually kickstart a digest, for lack of a better word\n\t\t\tif (animations.length === 1) {\n\t\t\t\tthis.requestAnimationFrame();\n\t\t\t}\n\t\t},\n\n\t\tcancelAnimation: function(chart) {\n\t\t\tvar index = helpers.findIndex(this.animations, function(animation) {\n\t\t\t\treturn animation.chart === chart;\n\t\t\t});\n\n\t\t\tif (index !== -1) {\n\t\t\t\tthis.animations.splice(index, 1);\n\t\t\t\tchart.animating = false;\n\t\t\t}\n\t\t},\n\n\t\trequestAnimationFrame: function() {\n\t\t\tvar me = this;\n\t\t\tif (me.request === null) {\n\t\t\t\t// Skip animation frame requests until the active one is executed.\n\t\t\t\t// This can happen when processing mouse events, e.g. 'mousemove'\n\t\t\t\t// and 'mouseout' events will trigger multiple renders.\n\t\t\t\tme.request = helpers.requestAnimFrame.call(window, function() {\n\t\t\t\t\tme.request = null;\n\t\t\t\t\tme.startDigest();\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tstartDigest: function() {\n\t\t\tvar me = this;\n\t\t\tvar startTime = Date.now();\n\t\t\tvar framesToDrop = 0;\n\n\t\t\tif (me.dropFrames > 1) {\n\t\t\t\tframesToDrop = Math.floor(me.dropFrames);\n\t\t\t\tme.dropFrames = me.dropFrames % 1;\n\t\t\t}\n\n\t\t\tme.advance(1 + framesToDrop);\n\n\t\t\tvar endTime = Date.now();\n\n\t\t\tme.dropFrames += (endTime - startTime) / me.frameDuration;\n\n\t\t\t// Do we have more stuff to animate?\n\t\t\tif (me.animations.length > 0) {\n\t\t\t\tme.requestAnimationFrame();\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tadvance: function(count) {\n\t\t\tvar animations = this.animations;\n\t\t\tvar animation, chart;\n\t\t\tvar i = 0;\n\n\t\t\twhile (i < animations.length) {\n\t\t\t\tanimation = animations[i];\n\t\t\t\tchart = animation.chart;\n\n\t\t\t\tanimation.currentStep = (animation.currentStep || 0) + count;\n\t\t\t\tanimation.currentStep = Math.min(animation.currentStep, animation.numSteps);\n\n\t\t\t\thelpers.callback(animation.render, [chart, animation], chart);\n\t\t\t\thelpers.callback(animation.onAnimationProgress, [animation], chart);\n\n\t\t\t\tif (animation.currentStep >= animation.numSteps) {\n\t\t\t\t\thelpers.callback(animation.onAnimationComplete, [animation], chart);\n\t\t\t\t\tchart.animating = false;\n\t\t\t\t\tanimations.splice(i, 1);\n\t\t\t\t} else {\n\t\t\t\t\t++i;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t};\n\n\t/**\n\t * Provided for backward compatibility, use Chart.Animation instead\n\t * @prop Chart.Animation#animationObject\n\t * @deprecated since version 2.6.0\n\t * @todo remove at version 3\n\t */\n\tObject.defineProperty(Chart.Animation.prototype, 'animationObject', {\n\t\tget: function() {\n\t\t\treturn this;\n\t\t}\n\t});\n\n\t/**\n\t * Provided for backward compatibility, use Chart.Animation#chart instead\n\t * @prop Chart.Animation#chartInstance\n\t * @deprecated since version 2.6.0\n\t * @todo remove at version 3\n\t */\n\tObject.defineProperty(Chart.Animation.prototype, 'chartInstance', {\n\t\tget: function() {\n\t\t\treturn this.chart;\n\t\t},\n\t\tset: function(value) {\n\t\t\tthis.chart = value;\n\t\t}\n\t});\n\n};\n\n},{\"25\":25,\"26\":26,\"45\":45}],23:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\nvar helpers = require(45);\nvar Interaction = require(28);\nvar layouts = require(30);\nvar platform = require(48);\nvar plugins = require(31);\n\nmodule.exports = function(Chart) {\n\n\t// Create a dictionary of chart types, to allow for extension of existing types\n\tChart.types = {};\n\n\t// Store a reference to each instance - allowing us to globally resize chart instances on window resize.\n\t// Destroy method on the chart will remove the instance of the chart from this reference.\n\tChart.instances = {};\n\n\t// Controllers available for dataset visualization eg. bar, line, slice, etc.\n\tChart.controllers = {};\n\n\t/**\n\t * Initializes the given config with global and chart default values.\n\t */\n\tfunction initConfig(config) {\n\t\tconfig = config || {};\n\n\t\t// Do NOT use configMerge() for the data object because this method merges arrays\n\t\t// and so would change references to labels and datasets, preventing data updates.\n\t\tvar data = config.data = config.data || {};\n\t\tdata.datasets = data.datasets || [];\n\t\tdata.labels = data.labels || [];\n\n\t\tconfig.options = helpers.configMerge(\n\t\t\tdefaults.global,\n\t\t\tdefaults[config.type],\n\t\t\tconfig.options || {});\n\n\t\treturn config;\n\t}\n\n\t/**\n\t * Updates the config of the chart\n\t * @param chart {Chart} chart to update the options for\n\t */\n\tfunction updateConfig(chart) {\n\t\tvar newOptions = chart.options;\n\n\t\thelpers.each(chart.scales, function(scale) {\n\t\t\tlayouts.removeBox(chart, scale);\n\t\t});\n\n\t\tnewOptions = helpers.configMerge(\n\t\t\tChart.defaults.global,\n\t\t\tChart.defaults[chart.config.type],\n\t\t\tnewOptions);\n\n\t\tchart.options = chart.config.options = newOptions;\n\t\tchart.ensureScalesHaveIDs();\n\t\tchart.buildOrUpdateScales();\n\t\t// Tooltip\n\t\tchart.tooltip._options = newOptions.tooltips;\n\t\tchart.tooltip.initialize();\n\t}\n\n\tfunction positionIsHorizontal(position) {\n\t\treturn position === 'top' || position === 'bottom';\n\t}\n\n\thelpers.extend(Chart.prototype, /** @lends Chart */ {\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tconstruct: function(item, config) {\n\t\t\tvar me = this;\n\n\t\t\tconfig = initConfig(config);\n\n\t\t\tvar context = platform.acquireContext(item, config);\n\t\t\tvar canvas = context && context.canvas;\n\t\t\tvar height = canvas && canvas.height;\n\t\t\tvar width = canvas && canvas.width;\n\n\t\t\tme.id = helpers.uid();\n\t\t\tme.ctx = context;\n\t\t\tme.canvas = canvas;\n\t\t\tme.config = config;\n\t\t\tme.width = width;\n\t\t\tme.height = height;\n\t\t\tme.aspectRatio = height ? width / height : null;\n\t\t\tme.options = config.options;\n\t\t\tme._bufferedRender = false;\n\n\t\t\t/**\n\t\t\t * Provided for backward compatibility, Chart and Chart.Controller have been merged,\n\t\t\t * the \"instance\" still need to be defined since it might be called from plugins.\n\t\t\t * @prop Chart#chart\n\t\t\t * @deprecated since version 2.6.0\n\t\t\t * @todo remove at version 3\n\t\t\t * @private\n\t\t\t */\n\t\t\tme.chart = me;\n\t\t\tme.controller = me; // chart.chart.controller #inception\n\n\t\t\t// Add the chart instance to the global namespace\n\t\t\tChart.instances[me.id] = me;\n\n\t\t\t// Define alias to the config data: `chart.data === chart.config.data`\n\t\t\tObject.defineProperty(me, 'data', {\n\t\t\t\tget: function() {\n\t\t\t\t\treturn me.config.data;\n\t\t\t\t},\n\t\t\t\tset: function(value) {\n\t\t\t\t\tme.config.data = value;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (!context || !canvas) {\n\t\t\t\t// The given item is not a compatible context2d element, let's return before finalizing\n\t\t\t\t// the chart initialization but after setting basic chart / controller properties that\n\t\t\t\t// can help to figure out that the chart is not valid (e.g chart.canvas !== null);\n\t\t\t\t// https://github.com/chartjs/Chart.js/issues/2807\n\t\t\t\tconsole.error(\"Failed to create chart: can't acquire context from the given item\");\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tme.initialize();\n\t\t\tme.update();\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tinitialize: function() {\n\t\t\tvar me = this;\n\n\t\t\t// Before init plugin notification\n\t\t\tplugins.notify(me, 'beforeInit');\n\n\t\t\thelpers.retinaScale(me, me.options.devicePixelRatio);\n\n\t\t\tme.bindEvents();\n\n\t\t\tif (me.options.responsive) {\n\t\t\t\t// Initial resize before chart draws (must be silent to preserve initial animations).\n\t\t\t\tme.resize(true);\n\t\t\t}\n\n\t\t\t// Make sure scales have IDs and are built before we build any controllers.\n\t\t\tme.ensureScalesHaveIDs();\n\t\t\tme.buildOrUpdateScales();\n\t\t\tme.initToolTip();\n\n\t\t\t// After init plugin notification\n\t\t\tplugins.notify(me, 'afterInit');\n\n\t\t\treturn me;\n\t\t},\n\n\t\tclear: function() {\n\t\t\thelpers.canvas.clear(this);\n\t\t\treturn this;\n\t\t},\n\n\t\tstop: function() {\n\t\t\t// Stops any current animation loop occurring\n\t\t\tChart.animationService.cancelAnimation(this);\n\t\t\treturn this;\n\t\t},\n\n\t\tresize: function(silent) {\n\t\t\tvar me = this;\n\t\t\tvar options = me.options;\n\t\t\tvar canvas = me.canvas;\n\t\t\tvar aspectRatio = (options.maintainAspectRatio && me.aspectRatio) || null;\n\n\t\t\t// the canvas render width and height will be casted to integers so make sure that\n\t\t\t// the canvas display style uses the same integer values to avoid blurring effect.\n\n\t\t\t// Set to 0 instead of canvas.size because the size defaults to 300x150 if the element is collased\n\t\t\tvar newWidth = Math.max(0, Math.floor(helpers.getMaximumWidth(canvas)));\n\t\t\tvar newHeight = Math.max(0, Math.floor(aspectRatio ? newWidth / aspectRatio : helpers.getMaximumHeight(canvas)));\n\n\t\t\tif (me.width === newWidth && me.height === newHeight) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tcanvas.width = me.width = newWidth;\n\t\t\tcanvas.height = me.height = newHeight;\n\t\t\tcanvas.style.width = newWidth + 'px';\n\t\t\tcanvas.style.height = newHeight + 'px';\n\n\t\t\thelpers.retinaScale(me, options.devicePixelRatio);\n\n\t\t\tif (!silent) {\n\t\t\t\t// Notify any plugins about the resize\n\t\t\t\tvar newSize = {width: newWidth, height: newHeight};\n\t\t\t\tplugins.notify(me, 'resize', [newSize]);\n\n\t\t\t\t// Notify of resize\n\t\t\t\tif (me.options.onResize) {\n\t\t\t\t\tme.options.onResize(me, newSize);\n\t\t\t\t}\n\n\t\t\t\tme.stop();\n\t\t\t\tme.update(me.options.responsiveAnimationDuration);\n\t\t\t}\n\t\t},\n\n\t\tensureScalesHaveIDs: function() {\n\t\t\tvar options = this.options;\n\t\t\tvar scalesOptions = options.scales || {};\n\t\t\tvar scaleOptions = options.scale;\n\n\t\t\thelpers.each(scalesOptions.xAxes, function(xAxisOptions, index) {\n\t\t\t\txAxisOptions.id = xAxisOptions.id || ('x-axis-' + index);\n\t\t\t});\n\n\t\t\thelpers.each(scalesOptions.yAxes, function(yAxisOptions, index) {\n\t\t\t\tyAxisOptions.id = yAxisOptions.id || ('y-axis-' + index);\n\t\t\t});\n\n\t\t\tif (scaleOptions) {\n\t\t\t\tscaleOptions.id = scaleOptions.id || 'scale';\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Builds a map of scale ID to scale object for future lookup.\n\t\t */\n\t\tbuildOrUpdateScales: function() {\n\t\t\tvar me = this;\n\t\t\tvar options = me.options;\n\t\t\tvar scales = me.scales || {};\n\t\t\tvar items = [];\n\t\t\tvar updated = Object.keys(scales).reduce(function(obj, id) {\n\t\t\t\tobj[id] = false;\n\t\t\t\treturn obj;\n\t\t\t}, {});\n\n\t\t\tif (options.scales) {\n\t\t\t\titems = items.concat(\n\t\t\t\t\t(options.scales.xAxes || []).map(function(xAxisOptions) {\n\t\t\t\t\t\treturn {options: xAxisOptions, dtype: 'category', dposition: 'bottom'};\n\t\t\t\t\t}),\n\t\t\t\t\t(options.scales.yAxes || []).map(function(yAxisOptions) {\n\t\t\t\t\t\treturn {options: yAxisOptions, dtype: 'linear', dposition: 'left'};\n\t\t\t\t\t})\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (options.scale) {\n\t\t\t\titems.push({\n\t\t\t\t\toptions: options.scale,\n\t\t\t\t\tdtype: 'radialLinear',\n\t\t\t\t\tisDefault: true,\n\t\t\t\t\tdposition: 'chartArea'\n\t\t\t\t});\n\t\t\t}\n\n\t\t\thelpers.each(items, function(item) {\n\t\t\t\tvar scaleOptions = item.options;\n\t\t\t\tvar id = scaleOptions.id;\n\t\t\t\tvar scaleType = helpers.valueOrDefault(scaleOptions.type, item.dtype);\n\n\t\t\t\tif (positionIsHorizontal(scaleOptions.position) !== positionIsHorizontal(item.dposition)) {\n\t\t\t\t\tscaleOptions.position = item.dposition;\n\t\t\t\t}\n\n\t\t\t\tupdated[id] = true;\n\t\t\t\tvar scale = null;\n\t\t\t\tif (id in scales && scales[id].type === scaleType) {\n\t\t\t\t\tscale = scales[id];\n\t\t\t\t\tscale.options = scaleOptions;\n\t\t\t\t\tscale.ctx = me.ctx;\n\t\t\t\t\tscale.chart = me;\n\t\t\t\t} else {\n\t\t\t\t\tvar scaleClass = Chart.scaleService.getScaleConstructor(scaleType);\n\t\t\t\t\tif (!scaleClass) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tscale = new scaleClass({\n\t\t\t\t\t\tid: id,\n\t\t\t\t\t\ttype: scaleType,\n\t\t\t\t\t\toptions: scaleOptions,\n\t\t\t\t\t\tctx: me.ctx,\n\t\t\t\t\t\tchart: me\n\t\t\t\t\t});\n\t\t\t\t\tscales[scale.id] = scale;\n\t\t\t\t}\n\n\t\t\t\tscale.mergeTicksOptions();\n\n\t\t\t\t// TODO(SB): I think we should be able to remove this custom case (options.scale)\n\t\t\t\t// and consider it as a regular scale part of the \"scales\"\" map only! This would\n\t\t\t\t// make the logic easier and remove some useless? custom code.\n\t\t\t\tif (item.isDefault) {\n\t\t\t\t\tme.scale = scale;\n\t\t\t\t}\n\t\t\t});\n\t\t\t// clear up discarded scales\n\t\t\thelpers.each(updated, function(hasUpdated, id) {\n\t\t\t\tif (!hasUpdated) {\n\t\t\t\t\tdelete scales[id];\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tme.scales = scales;\n\n\t\t\tChart.scaleService.addScalesToLayout(this);\n\t\t},\n\n\t\tbuildOrUpdateControllers: function() {\n\t\t\tvar me = this;\n\t\t\tvar types = [];\n\t\t\tvar newControllers = [];\n\n\t\t\thelpers.each(me.data.datasets, function(dataset, datasetIndex) {\n\t\t\t\tvar meta = me.getDatasetMeta(datasetIndex);\n\t\t\t\tvar type = dataset.type || me.config.type;\n\n\t\t\t\tif (meta.type && meta.type !== type) {\n\t\t\t\t\tme.destroyDatasetMeta(datasetIndex);\n\t\t\t\t\tmeta = me.getDatasetMeta(datasetIndex);\n\t\t\t\t}\n\t\t\t\tmeta.type = type;\n\n\t\t\t\ttypes.push(meta.type);\n\n\t\t\t\tif (meta.controller) {\n\t\t\t\t\tmeta.controller.updateIndex(datasetIndex);\n\t\t\t\t\tmeta.controller.linkScales();\n\t\t\t\t} else {\n\t\t\t\t\tvar ControllerClass = Chart.controllers[meta.type];\n\t\t\t\t\tif (ControllerClass === undefined) {\n\t\t\t\t\t\tthrow new Error('\"' + meta.type + '\" is not a chart type.');\n\t\t\t\t\t}\n\n\t\t\t\t\tmeta.controller = new ControllerClass(me, datasetIndex);\n\t\t\t\t\tnewControllers.push(meta.controller);\n\t\t\t\t}\n\t\t\t}, me);\n\n\t\t\treturn newControllers;\n\t\t},\n\n\t\t/**\n\t\t * Reset the elements of all datasets\n\t\t * @private\n\t\t */\n\t\tresetElements: function() {\n\t\t\tvar me = this;\n\t\t\thelpers.each(me.data.datasets, function(dataset, datasetIndex) {\n\t\t\t\tme.getDatasetMeta(datasetIndex).controller.reset();\n\t\t\t}, me);\n\t\t},\n\n\t\t/**\n\t\t* Resets the chart back to it's state before the initial animation\n\t\t*/\n\t\treset: function() {\n\t\t\tthis.resetElements();\n\t\t\tthis.tooltip.initialize();\n\t\t},\n\n\t\tupdate: function(config) {\n\t\t\tvar me = this;\n\n\t\t\tif (!config || typeof config !== 'object') {\n\t\t\t\t// backwards compatibility\n\t\t\t\tconfig = {\n\t\t\t\t\tduration: config,\n\t\t\t\t\tlazy: arguments[1]\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tupdateConfig(me);\n\n\t\t\t// plugins options references might have change, let's invalidate the cache\n\t\t\t// https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167\n\t\t\tplugins._invalidate(me);\n\n\t\t\tif (plugins.notify(me, 'beforeUpdate') === false) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// In case the entire data object changed\n\t\t\tme.tooltip._data = me.data;\n\n\t\t\t// Make sure dataset controllers are updated and new controllers are reset\n\t\t\tvar newControllers = me.buildOrUpdateControllers();\n\n\t\t\t// Make sure all dataset controllers have correct meta data counts\n\t\t\thelpers.each(me.data.datasets, function(dataset, datasetIndex) {\n\t\t\t\tme.getDatasetMeta(datasetIndex).controller.buildOrUpdateElements();\n\t\t\t}, me);\n\n\t\t\tme.updateLayout();\n\n\t\t\t// Can only reset the new controllers after the scales have been updated\n\t\t\tif (me.options.animation && me.options.animation.duration) {\n\t\t\t\thelpers.each(newControllers, function(controller) {\n\t\t\t\t\tcontroller.reset();\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tme.updateDatasets();\n\n\t\t\t// Need to reset tooltip in case it is displayed with elements that are removed\n\t\t\t// after update.\n\t\t\tme.tooltip.initialize();\n\n\t\t\t// Last active contains items that were previously in the tooltip.\n\t\t\t// When we reset the tooltip, we need to clear it\n\t\t\tme.lastActive = [];\n\n\t\t\t// Do this before render so that any plugins that need final scale updates can use it\n\t\t\tplugins.notify(me, 'afterUpdate');\n\n\t\t\tif (me._bufferedRender) {\n\t\t\t\tme._bufferedRequest = {\n\t\t\t\t\tduration: config.duration,\n\t\t\t\t\teasing: config.easing,\n\t\t\t\t\tlazy: config.lazy\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tme.render(config);\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Updates the chart layout unless a plugin returns `false` to the `beforeLayout`\n\t\t * hook, in which case, plugins will not be called on `afterLayout`.\n\t\t * @private\n\t\t */\n\t\tupdateLayout: function() {\n\t\t\tvar me = this;\n\n\t\t\tif (plugins.notify(me, 'beforeLayout') === false) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tlayouts.update(this, this.width, this.height);\n\n\t\t\t/**\n\t\t\t * Provided for backward compatibility, use `afterLayout` instead.\n\t\t\t * @method IPlugin#afterScaleUpdate\n\t\t\t * @deprecated since version 2.5.0\n\t\t\t * @todo remove at version 3\n\t\t\t * @private\n\t\t\t */\n\t\t\tplugins.notify(me, 'afterScaleUpdate');\n\t\t\tplugins.notify(me, 'afterLayout');\n\t\t},\n\n\t\t/**\n\t\t * Updates all datasets unless a plugin returns `false` to the `beforeDatasetsUpdate`\n\t\t * hook, in which case, plugins will not be called on `afterDatasetsUpdate`.\n\t\t * @private\n\t\t */\n\t\tupdateDatasets: function() {\n\t\t\tvar me = this;\n\n\t\t\tif (plugins.notify(me, 'beforeDatasetsUpdate') === false) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfor (var i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {\n\t\t\t\tme.updateDataset(i);\n\t\t\t}\n\n\t\t\tplugins.notify(me, 'afterDatasetsUpdate');\n\t\t},\n\n\t\t/**\n\t\t * Updates dataset at index unless a plugin returns `false` to the `beforeDatasetUpdate`\n\t\t * hook, in which case, plugins will not be called on `afterDatasetUpdate`.\n\t\t * @private\n\t\t */\n\t\tupdateDataset: function(index) {\n\t\t\tvar me = this;\n\t\t\tvar meta = me.getDatasetMeta(index);\n\t\t\tvar args = {\n\t\t\t\tmeta: meta,\n\t\t\t\tindex: index\n\t\t\t};\n\n\t\t\tif (plugins.notify(me, 'beforeDatasetUpdate', [args]) === false) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tmeta.controller.update();\n\n\t\t\tplugins.notify(me, 'afterDatasetUpdate', [args]);\n\t\t},\n\n\t\trender: function(config) {\n\t\t\tvar me = this;\n\n\t\t\tif (!config || typeof config !== 'object') {\n\t\t\t\t// backwards compatibility\n\t\t\t\tconfig = {\n\t\t\t\t\tduration: config,\n\t\t\t\t\tlazy: arguments[1]\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tvar duration = config.duration;\n\t\t\tvar lazy = config.lazy;\n\n\t\t\tif (plugins.notify(me, 'beforeRender') === false) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar animationOptions = me.options.animation;\n\t\t\tvar onComplete = function(animation) {\n\t\t\t\tplugins.notify(me, 'afterRender');\n\t\t\t\thelpers.callback(animationOptions && animationOptions.onComplete, [animation], me);\n\t\t\t};\n\n\t\t\tif (animationOptions && ((typeof duration !== 'undefined' && duration !== 0) || (typeof duration === 'undefined' && animationOptions.duration !== 0))) {\n\t\t\t\tvar animation = new Chart.Animation({\n\t\t\t\t\tnumSteps: (duration || animationOptions.duration) / 16.66, // 60 fps\n\t\t\t\t\teasing: config.easing || animationOptions.easing,\n\n\t\t\t\t\trender: function(chart, animationObject) {\n\t\t\t\t\t\tvar easingFunction = helpers.easing.effects[animationObject.easing];\n\t\t\t\t\t\tvar currentStep = animationObject.currentStep;\n\t\t\t\t\t\tvar stepDecimal = currentStep / animationObject.numSteps;\n\n\t\t\t\t\t\tchart.draw(easingFunction(stepDecimal), stepDecimal, currentStep);\n\t\t\t\t\t},\n\n\t\t\t\t\tonAnimationProgress: animationOptions.onProgress,\n\t\t\t\t\tonAnimationComplete: onComplete\n\t\t\t\t});\n\n\t\t\t\tChart.animationService.addAnimation(me, animation, duration, lazy);\n\t\t\t} else {\n\t\t\t\tme.draw();\n\n\t\t\t\t// See https://github.com/chartjs/Chart.js/issues/3781\n\t\t\t\tonComplete(new Chart.Animation({numSteps: 0, chart: me}));\n\t\t\t}\n\n\t\t\treturn me;\n\t\t},\n\n\t\tdraw: function(easingValue) {\n\t\t\tvar me = this;\n\n\t\t\tme.clear();\n\n\t\t\tif (helpers.isNullOrUndef(easingValue)) {\n\t\t\t\teasingValue = 1;\n\t\t\t}\n\n\t\t\tme.transition(easingValue);\n\n\t\t\tif (plugins.notify(me, 'beforeDraw', [easingValue]) === false) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Draw all the scales\n\t\t\thelpers.each(me.boxes, function(box) {\n\t\t\t\tbox.draw(me.chartArea);\n\t\t\t}, me);\n\n\t\t\tif (me.scale) {\n\t\t\t\tme.scale.draw();\n\t\t\t}\n\n\t\t\tme.drawDatasets(easingValue);\n\t\t\tme._drawTooltip(easingValue);\n\n\t\t\tplugins.notify(me, 'afterDraw', [easingValue]);\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\ttransition: function(easingValue) {\n\t\t\tvar me = this;\n\n\t\t\tfor (var i = 0, ilen = (me.data.datasets || []).length; i < ilen; ++i) {\n\t\t\t\tif (me.isDatasetVisible(i)) {\n\t\t\t\t\tme.getDatasetMeta(i).controller.transition(easingValue);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tme.tooltip.transition(easingValue);\n\t\t},\n\n\t\t/**\n\t\t * Draws all datasets unless a plugin returns `false` to the `beforeDatasetsDraw`\n\t\t * hook, in which case, plugins will not be called on `afterDatasetsDraw`.\n\t\t * @private\n\t\t */\n\t\tdrawDatasets: function(easingValue) {\n\t\t\tvar me = this;\n\n\t\t\tif (plugins.notify(me, 'beforeDatasetsDraw', [easingValue]) === false) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Draw datasets reversed to support proper line stacking\n\t\t\tfor (var i = (me.data.datasets || []).length - 1; i >= 0; --i) {\n\t\t\t\tif (me.isDatasetVisible(i)) {\n\t\t\t\t\tme.drawDataset(i, easingValue);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tplugins.notify(me, 'afterDatasetsDraw', [easingValue]);\n\t\t},\n\n\t\t/**\n\t\t * Draws dataset at index unless a plugin returns `false` to the `beforeDatasetDraw`\n\t\t * hook, in which case, plugins will not be called on `afterDatasetDraw`.\n\t\t * @private\n\t\t */\n\t\tdrawDataset: function(index, easingValue) {\n\t\t\tvar me = this;\n\t\t\tvar meta = me.getDatasetMeta(index);\n\t\t\tvar args = {\n\t\t\t\tmeta: meta,\n\t\t\t\tindex: index,\n\t\t\t\teasingValue: easingValue\n\t\t\t};\n\n\t\t\tif (plugins.notify(me, 'beforeDatasetDraw', [args]) === false) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tmeta.controller.draw(easingValue);\n\n\t\t\tplugins.notify(me, 'afterDatasetDraw', [args]);\n\t\t},\n\n\t\t/**\n\t\t * Draws tooltip unless a plugin returns `false` to the `beforeTooltipDraw`\n\t\t * hook, in which case, plugins will not be called on `afterTooltipDraw`.\n\t\t * @private\n\t\t */\n\t\t_drawTooltip: function(easingValue) {\n\t\t\tvar me = this;\n\t\t\tvar tooltip = me.tooltip;\n\t\t\tvar args = {\n\t\t\t\ttooltip: tooltip,\n\t\t\t\teasingValue: easingValue\n\t\t\t};\n\n\t\t\tif (plugins.notify(me, 'beforeTooltipDraw', [args]) === false) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttooltip.draw();\n\n\t\t\tplugins.notify(me, 'afterTooltipDraw', [args]);\n\t\t},\n\n\t\t// Get the single element that was clicked on\n\t\t// @return : An object containing the dataset index and element index of the matching element. Also contains the rectangle that was draw\n\t\tgetElementAtEvent: function(e) {\n\t\t\treturn Interaction.modes.single(this, e);\n\t\t},\n\n\t\tgetElementsAtEvent: function(e) {\n\t\t\treturn Interaction.modes.label(this, e, {intersect: true});\n\t\t},\n\n\t\tgetElementsAtXAxis: function(e) {\n\t\t\treturn Interaction.modes['x-axis'](this, e, {intersect: true});\n\t\t},\n\n\t\tgetElementsAtEventForMode: function(e, mode, options) {\n\t\t\tvar method = Interaction.modes[mode];\n\t\t\tif (typeof method === 'function') {\n\t\t\t\treturn method(this, e, options);\n\t\t\t}\n\n\t\t\treturn [];\n\t\t},\n\n\t\tgetDatasetAtEvent: function(e) {\n\t\t\treturn Interaction.modes.dataset(this, e, {intersect: true});\n\t\t},\n\n\t\tgetDatasetMeta: function(datasetIndex) {\n\t\t\tvar me = this;\n\t\t\tvar dataset = me.data.datasets[datasetIndex];\n\t\t\tif (!dataset._meta) {\n\t\t\t\tdataset._meta = {};\n\t\t\t}\n\n\t\t\tvar meta = dataset._meta[me.id];\n\t\t\tif (!meta) {\n\t\t\t\tmeta = dataset._meta[me.id] = {\n\t\t\t\t\ttype: null,\n\t\t\t\t\tdata: [],\n\t\t\t\t\tdataset: null,\n\t\t\t\t\tcontroller: null,\n\t\t\t\t\thidden: null,\t\t\t// See isDatasetVisible() comment\n\t\t\t\t\txAxisID: null,\n\t\t\t\t\tyAxisID: null\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn meta;\n\t\t},\n\n\t\tgetVisibleDatasetCount: function() {\n\t\t\tvar count = 0;\n\t\t\tfor (var i = 0, ilen = this.data.datasets.length; i < ilen; ++i) {\n\t\t\t\tif (this.isDatasetVisible(i)) {\n\t\t\t\t\tcount++;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn count;\n\t\t},\n\n\t\tisDatasetVisible: function(datasetIndex) {\n\t\t\tvar meta = this.getDatasetMeta(datasetIndex);\n\n\t\t\t// meta.hidden is a per chart dataset hidden flag override with 3 states: if true or false,\n\t\t\t// the dataset.hidden value is ignored, else if null, the dataset hidden state is returned.\n\t\t\treturn typeof meta.hidden === 'boolean' ? !meta.hidden : !this.data.datasets[datasetIndex].hidden;\n\t\t},\n\n\t\tgenerateLegend: function() {\n\t\t\treturn this.options.legendCallback(this);\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tdestroyDatasetMeta: function(datasetIndex) {\n\t\t\tvar id = this.id;\n\t\t\tvar dataset = this.data.datasets[datasetIndex];\n\t\t\tvar meta = dataset._meta && dataset._meta[id];\n\n\t\t\tif (meta) {\n\t\t\t\tmeta.controller.destroy();\n\t\t\t\tdelete dataset._meta[id];\n\t\t\t}\n\t\t},\n\n\t\tdestroy: function() {\n\t\t\tvar me = this;\n\t\t\tvar canvas = me.canvas;\n\t\t\tvar i, ilen;\n\n\t\t\tme.stop();\n\n\t\t\t// dataset controllers need to cleanup associated data\n\t\t\tfor (i = 0, ilen = me.data.datasets.length; i < ilen; ++i) {\n\t\t\t\tme.destroyDatasetMeta(i);\n\t\t\t}\n\n\t\t\tif (canvas) {\n\t\t\t\tme.unbindEvents();\n\t\t\t\thelpers.canvas.clear(me);\n\t\t\t\tplatform.releaseContext(me.ctx);\n\t\t\t\tme.canvas = null;\n\t\t\t\tme.ctx = null;\n\t\t\t}\n\n\t\t\tplugins.notify(me, 'destroy');\n\n\t\t\tdelete Chart.instances[me.id];\n\t\t},\n\n\t\ttoBase64Image: function() {\n\t\t\treturn this.canvas.toDataURL.apply(this.canvas, arguments);\n\t\t},\n\n\t\tinitToolTip: function() {\n\t\t\tvar me = this;\n\t\t\tme.tooltip = new Chart.Tooltip({\n\t\t\t\t_chart: me,\n\t\t\t\t_chartInstance: me, // deprecated, backward compatibility\n\t\t\t\t_data: me.data,\n\t\t\t\t_options: me.options.tooltips\n\t\t\t}, me);\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tbindEvents: function() {\n\t\t\tvar me = this;\n\t\t\tvar listeners = me._listeners = {};\n\t\t\tvar listener = function() {\n\t\t\t\tme.eventHandler.apply(me, arguments);\n\t\t\t};\n\n\t\t\thelpers.each(me.options.events, function(type) {\n\t\t\t\tplatform.addEventListener(me, type, listener);\n\t\t\t\tlisteners[type] = listener;\n\t\t\t});\n\n\t\t\t// Elements used to detect size change should not be injected for non responsive charts.\n\t\t\t// See https://github.com/chartjs/Chart.js/issues/2210\n\t\t\tif (me.options.responsive) {\n\t\t\t\tlistener = function() {\n\t\t\t\t\tme.resize();\n\t\t\t\t};\n\n\t\t\t\tplatform.addEventListener(me, 'resize', listener);\n\t\t\t\tlisteners.resize = listener;\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tunbindEvents: function() {\n\t\t\tvar me = this;\n\t\t\tvar listeners = me._listeners;\n\t\t\tif (!listeners) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tdelete me._listeners;\n\t\t\thelpers.each(listeners, function(listener, type) {\n\t\t\t\tplatform.removeEventListener(me, type, listener);\n\t\t\t});\n\t\t},\n\n\t\tupdateHoverStyle: function(elements, mode, enabled) {\n\t\t\tvar method = enabled ? 'setHoverStyle' : 'removeHoverStyle';\n\t\t\tvar element, i, ilen;\n\n\t\t\tfor (i = 0, ilen = elements.length; i < ilen; ++i) {\n\t\t\t\telement = elements[i];\n\t\t\t\tif (element) {\n\t\t\t\t\tthis.getDatasetMeta(element._datasetIndex).controller[method](element);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\teventHandler: function(e) {\n\t\t\tvar me = this;\n\t\t\tvar tooltip = me.tooltip;\n\n\t\t\tif (plugins.notify(me, 'beforeEvent', [e]) === false) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Buffer any update calls so that renders do not occur\n\t\t\tme._bufferedRender = true;\n\t\t\tme._bufferedRequest = null;\n\n\t\t\tvar changed = me.handleEvent(e);\n\t\t\t// for smooth tooltip animations issue #4989\n\t\t\t// the tooltip should be the source of change\n\t\t\t// Animation check workaround:\n\t\t\t// tooltip._start will be null when tooltip isn't animating\n\t\t\tif (tooltip) {\n\t\t\t\tchanged = tooltip._start\n\t\t\t\t\t? tooltip.handleEvent(e)\n\t\t\t\t\t: changed | tooltip.handleEvent(e);\n\t\t\t}\n\n\t\t\tplugins.notify(me, 'afterEvent', [e]);\n\n\t\t\tvar bufferedRequest = me._bufferedRequest;\n\t\t\tif (bufferedRequest) {\n\t\t\t\t// If we have an update that was triggered, we need to do a normal render\n\t\t\t\tme.render(bufferedRequest);\n\t\t\t} else if (changed && !me.animating) {\n\t\t\t\t// If entering, leaving, or changing elements, animate the change via pivot\n\t\t\t\tme.stop();\n\n\t\t\t\t// We only need to render at this point. Updating will cause scales to be\n\t\t\t\t// recomputed generating flicker & using more memory than necessary.\n\t\t\t\tme.render(me.options.hover.animationDuration, true);\n\t\t\t}\n\n\t\t\tme._bufferedRender = false;\n\t\t\tme._bufferedRequest = null;\n\n\t\t\treturn me;\n\t\t},\n\n\t\t/**\n\t\t * Handle an event\n\t\t * @private\n\t\t * @param {IEvent} event the event to handle\n\t\t * @return {Boolean} true if the chart needs to re-render\n\t\t */\n\t\thandleEvent: function(e) {\n\t\t\tvar me = this;\n\t\t\tvar options = me.options || {};\n\t\t\tvar hoverOptions = options.hover;\n\t\t\tvar changed = false;\n\n\t\t\tme.lastActive = me.lastActive || [];\n\n\t\t\t// Find Active Elements for hover and tooltips\n\t\t\tif (e.type === 'mouseout') {\n\t\t\t\tme.active = [];\n\t\t\t} else {\n\t\t\t\tme.active = me.getElementsAtEventForMode(e, hoverOptions.mode, hoverOptions);\n\t\t\t}\n\n\t\t\t// Invoke onHover hook\n\t\t\t// Need to call with native event here to not break backwards compatibility\n\t\t\thelpers.callback(options.onHover || options.hover.onHover, [e.native, me.active], me);\n\n\t\t\tif (e.type === 'mouseup' || e.type === 'click') {\n\t\t\t\tif (options.onClick) {\n\t\t\t\t\t// Use e.native here for backwards compatibility\n\t\t\t\t\toptions.onClick.call(me, e.native, me.active);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Remove styling for last active (even if it may still be active)\n\t\t\tif (me.lastActive.length) {\n\t\t\t\tme.updateHoverStyle(me.lastActive, hoverOptions.mode, false);\n\t\t\t}\n\n\t\t\t// Built in hover styling\n\t\t\tif (me.active.length && hoverOptions.mode) {\n\t\t\t\tme.updateHoverStyle(me.active, hoverOptions.mode, true);\n\t\t\t}\n\n\t\t\tchanged = !helpers.arrayEquals(me.active, me.lastActive);\n\n\t\t\t// Remember Last Actives\n\t\t\tme.lastActive = me.active;\n\n\t\t\treturn changed;\n\t\t}\n\t});\n\n\t/**\n\t * Provided for backward compatibility, use Chart instead.\n\t * @class Chart.Controller\n\t * @deprecated since version 2.6.0\n\t * @todo remove at version 3\n\t * @private\n\t */\n\tChart.Controller = Chart;\n};\n\n},{\"25\":25,\"28\":28,\"30\":30,\"31\":31,\"45\":45,\"48\":48}],24:[function(require,module,exports){\n'use strict';\n\nvar helpers = require(45);\n\nmodule.exports = function(Chart) {\n\n\tvar arrayEvents = ['push', 'pop', 'shift', 'splice', 'unshift'];\n\n\t/**\n\t * Hooks the array methods that add or remove values ('push', pop', 'shift', 'splice',\n\t * 'unshift') and notify the listener AFTER the array has been altered. Listeners are\n\t * called on the 'onData*' callbacks (e.g. onDataPush, etc.) with same arguments.\n\t */\n\tfunction listenArrayEvents(array, listener) {\n\t\tif (array._chartjs) {\n\t\t\tarray._chartjs.listeners.push(listener);\n\t\t\treturn;\n\t\t}\n\n\t\tObject.defineProperty(array, '_chartjs', {\n\t\t\tconfigurable: true,\n\t\t\tenumerable: false,\n\t\t\tvalue: {\n\t\t\t\tlisteners: [listener]\n\t\t\t}\n\t\t});\n\n\t\tarrayEvents.forEach(function(key) {\n\t\t\tvar method = 'onData' + key.charAt(0).toUpperCase() + key.slice(1);\n\t\t\tvar base = array[key];\n\n\t\t\tObject.defineProperty(array, key, {\n\t\t\t\tconfigurable: true,\n\t\t\t\tenumerable: false,\n\t\t\t\tvalue: function() {\n\t\t\t\t\tvar args = Array.prototype.slice.call(arguments);\n\t\t\t\t\tvar res = base.apply(this, args);\n\n\t\t\t\t\thelpers.each(array._chartjs.listeners, function(object) {\n\t\t\t\t\t\tif (typeof object[method] === 'function') {\n\t\t\t\t\t\t\tobject[method].apply(object, args);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\treturn res;\n\t\t\t\t}\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Removes the given array event listener and cleanup extra attached properties (such as\n\t * the _chartjs stub and overridden methods) if array doesn't have any more listeners.\n\t */\n\tfunction unlistenArrayEvents(array, listener) {\n\t\tvar stub = array._chartjs;\n\t\tif (!stub) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar listeners = stub.listeners;\n\t\tvar index = listeners.indexOf(listener);\n\t\tif (index !== -1) {\n\t\t\tlisteners.splice(index, 1);\n\t\t}\n\n\t\tif (listeners.length > 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tarrayEvents.forEach(function(key) {\n\t\t\tdelete array[key];\n\t\t});\n\n\t\tdelete array._chartjs;\n\t}\n\n\t// Base class for all dataset controllers (line, bar, etc)\n\tChart.DatasetController = function(chart, datasetIndex) {\n\t\tthis.initialize(chart, datasetIndex);\n\t};\n\n\thelpers.extend(Chart.DatasetController.prototype, {\n\n\t\t/**\n\t\t * Element type used to generate a meta dataset (e.g. Chart.element.Line).\n\t\t * @type {Chart.core.element}\n\t\t */\n\t\tdatasetElementType: null,\n\n\t\t/**\n\t\t * Element type used to generate a meta data (e.g. Chart.element.Point).\n\t\t * @type {Chart.core.element}\n\t\t */\n\t\tdataElementType: null,\n\n\t\tinitialize: function(chart, datasetIndex) {\n\t\t\tvar me = this;\n\t\t\tme.chart = chart;\n\t\t\tme.index = datasetIndex;\n\t\t\tme.linkScales();\n\t\t\tme.addElements();\n\t\t},\n\n\t\tupdateIndex: function(datasetIndex) {\n\t\t\tthis.index = datasetIndex;\n\t\t},\n\n\t\tlinkScales: function() {\n\t\t\tvar me = this;\n\t\t\tvar meta = me.getMeta();\n\t\t\tvar dataset = me.getDataset();\n\n\t\t\tif (meta.xAxisID === null || !(meta.xAxisID in me.chart.scales)) {\n\t\t\t\tmeta.xAxisID = dataset.xAxisID || me.chart.options.scales.xAxes[0].id;\n\t\t\t}\n\t\t\tif (meta.yAxisID === null || !(meta.yAxisID in me.chart.scales)) {\n\t\t\t\tmeta.yAxisID = dataset.yAxisID || me.chart.options.scales.yAxes[0].id;\n\t\t\t}\n\t\t},\n\n\t\tgetDataset: function() {\n\t\t\treturn this.chart.data.datasets[this.index];\n\t\t},\n\n\t\tgetMeta: function() {\n\t\t\treturn this.chart.getDatasetMeta(this.index);\n\t\t},\n\n\t\tgetScaleForId: function(scaleID) {\n\t\t\treturn this.chart.scales[scaleID];\n\t\t},\n\n\t\treset: function() {\n\t\t\tthis.update(true);\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tdestroy: function() {\n\t\t\tif (this._data) {\n\t\t\t\tunlistenArrayEvents(this._data, this);\n\t\t\t}\n\t\t},\n\n\t\tcreateMetaDataset: function() {\n\t\t\tvar me = this;\n\t\t\tvar type = me.datasetElementType;\n\t\t\treturn type && new type({\n\t\t\t\t_chart: me.chart,\n\t\t\t\t_datasetIndex: me.index\n\t\t\t});\n\t\t},\n\n\t\tcreateMetaData: function(index) {\n\t\t\tvar me = this;\n\t\t\tvar type = me.dataElementType;\n\t\t\treturn type && new type({\n\t\t\t\t_chart: me.chart,\n\t\t\t\t_datasetIndex: me.index,\n\t\t\t\t_index: index\n\t\t\t});\n\t\t},\n\n\t\taddElements: function() {\n\t\t\tvar me = this;\n\t\t\tvar meta = me.getMeta();\n\t\t\tvar data = me.getDataset().data || [];\n\t\t\tvar metaData = meta.data;\n\t\t\tvar i, ilen;\n\n\t\t\tfor (i = 0, ilen = data.length; i < ilen; ++i) {\n\t\t\t\tmetaData[i] = metaData[i] || me.createMetaData(i);\n\t\t\t}\n\n\t\t\tmeta.dataset = meta.dataset || me.createMetaDataset();\n\t\t},\n\n\t\taddElementAndReset: function(index) {\n\t\t\tvar element = this.createMetaData(index);\n\t\t\tthis.getMeta().data.splice(index, 0, element);\n\t\t\tthis.updateElement(element, index, true);\n\t\t},\n\n\t\tbuildOrUpdateElements: function() {\n\t\t\tvar me = this;\n\t\t\tvar dataset = me.getDataset();\n\t\t\tvar data = dataset.data || (dataset.data = []);\n\n\t\t\t// In order to correctly handle data addition/deletion animation (an thus simulate\n\t\t\t// real-time charts), we need to monitor these data modifications and synchronize\n\t\t\t// the internal meta data accordingly.\n\t\t\tif (me._data !== data) {\n\t\t\t\tif (me._data) {\n\t\t\t\t\t// This case happens when the user replaced the data array instance.\n\t\t\t\t\tunlistenArrayEvents(me._data, me);\n\t\t\t\t}\n\n\t\t\t\tlistenArrayEvents(data, me);\n\t\t\t\tme._data = data;\n\t\t\t}\n\n\t\t\t// Re-sync meta data in case the user replaced the data array or if we missed\n\t\t\t// any updates and so make sure that we handle number of datapoints changing.\n\t\t\tme.resyncElements();\n\t\t},\n\n\t\tupdate: helpers.noop,\n\n\t\ttransition: function(easingValue) {\n\t\t\tvar meta = this.getMeta();\n\t\t\tvar elements = meta.data || [];\n\t\t\tvar ilen = elements.length;\n\t\t\tvar i = 0;\n\n\t\t\tfor (; i < ilen; ++i) {\n\t\t\t\telements[i].transition(easingValue);\n\t\t\t}\n\n\t\t\tif (meta.dataset) {\n\t\t\t\tmeta.dataset.transition(easingValue);\n\t\t\t}\n\t\t},\n\n\t\tdraw: function() {\n\t\t\tvar meta = this.getMeta();\n\t\t\tvar elements = meta.data || [];\n\t\t\tvar ilen = elements.length;\n\t\t\tvar i = 0;\n\n\t\t\tif (meta.dataset) {\n\t\t\t\tmeta.dataset.draw();\n\t\t\t}\n\n\t\t\tfor (; i < ilen; ++i) {\n\t\t\t\telements[i].draw();\n\t\t\t}\n\t\t},\n\n\t\tremoveHoverStyle: function(element, elementOpts) {\n\t\t\tvar dataset = this.chart.data.datasets[element._datasetIndex];\n\t\t\tvar index = element._index;\n\t\t\tvar custom = element.custom || {};\n\t\t\tvar valueOrDefault = helpers.valueAtIndexOrDefault;\n\t\t\tvar model = element._model;\n\n\t\t\tmodel.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);\n\t\t\tmodel.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);\n\t\t\tmodel.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);\n\t\t},\n\n\t\tsetHoverStyle: function(element) {\n\t\t\tvar dataset = this.chart.data.datasets[element._datasetIndex];\n\t\t\tvar index = element._index;\n\t\t\tvar custom = element.custom || {};\n\t\t\tvar valueOrDefault = helpers.valueAtIndexOrDefault;\n\t\t\tvar getHoverColor = helpers.getHoverColor;\n\t\t\tvar model = element._model;\n\n\t\t\tmodel.backgroundColor = custom.hoverBackgroundColor ? custom.hoverBackgroundColor : valueOrDefault(dataset.hoverBackgroundColor, index, getHoverColor(model.backgroundColor));\n\t\t\tmodel.borderColor = custom.hoverBorderColor ? custom.hoverBorderColor : valueOrDefault(dataset.hoverBorderColor, index, getHoverColor(model.borderColor));\n\t\t\tmodel.borderWidth = custom.hoverBorderWidth ? custom.hoverBorderWidth : valueOrDefault(dataset.hoverBorderWidth, index, model.borderWidth);\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tresyncElements: function() {\n\t\t\tvar me = this;\n\t\t\tvar meta = me.getMeta();\n\t\t\tvar data = me.getDataset().data;\n\t\t\tvar numMeta = meta.data.length;\n\t\t\tvar numData = data.length;\n\n\t\t\tif (numData < numMeta) {\n\t\t\t\tmeta.data.splice(numData, numMeta - numData);\n\t\t\t} else if (numData > numMeta) {\n\t\t\t\tme.insertElements(numMeta, numData - numMeta);\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tinsertElements: function(start, count) {\n\t\t\tfor (var i = 0; i < count; ++i) {\n\t\t\t\tthis.addElementAndReset(start + i);\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tonDataPush: function() {\n\t\t\tthis.insertElements(this.getDataset().data.length - 1, arguments.length);\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tonDataPop: function() {\n\t\t\tthis.getMeta().data.pop();\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tonDataShift: function() {\n\t\t\tthis.getMeta().data.shift();\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tonDataSplice: function(start, count) {\n\t\t\tthis.getMeta().data.splice(start, count);\n\t\t\tthis.insertElements(start, arguments.length - 2);\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tonDataUnshift: function() {\n\t\t\tthis.insertElements(0, arguments.length);\n\t\t}\n\t});\n\n\tChart.DatasetController.extend = helpers.inherits;\n};\n\n},{\"45\":45}],25:[function(require,module,exports){\n'use strict';\n\nvar helpers = require(45);\n\nmodule.exports = {\n\t/**\n\t * @private\n\t */\n\t_set: function(scope, values) {\n\t\treturn helpers.merge(this[scope] || (this[scope] = {}), values);\n\t}\n};\n\n},{\"45\":45}],26:[function(require,module,exports){\n'use strict';\n\nvar color = require(3);\nvar helpers = require(45);\n\nfunction interpolate(start, view, model, ease) {\n\tvar keys = Object.keys(model);\n\tvar i, ilen, key, actual, origin, target, type, c0, c1;\n\n\tfor (i = 0, ilen = keys.length; i < ilen; ++i) {\n\t\tkey = keys[i];\n\n\t\ttarget = model[key];\n\n\t\t// if a value is added to the model after pivot() has been called, the view\n\t\t// doesn't contain it, so let's initialize the view to the target value.\n\t\tif (!view.hasOwnProperty(key)) {\n\t\t\tview[key] = target;\n\t\t}\n\n\t\tactual = view[key];\n\n\t\tif (actual === target || key[0] === '_') {\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (!start.hasOwnProperty(key)) {\n\t\t\tstart[key] = actual;\n\t\t}\n\n\t\torigin = start[key];\n\n\t\ttype = typeof target;\n\n\t\tif (type === typeof origin) {\n\t\t\tif (type === 'string') {\n\t\t\t\tc0 = color(origin);\n\t\t\t\tif (c0.valid) {\n\t\t\t\t\tc1 = color(target);\n\t\t\t\t\tif (c1.valid) {\n\t\t\t\t\t\tview[key] = c1.mix(c0, ease).rgbString();\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else if (type === 'number' && isFinite(origin) && isFinite(target)) {\n\t\t\t\tview[key] = origin + (target - origin) * ease;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tview[key] = target;\n\t}\n}\n\nvar Element = function(configuration) {\n\thelpers.extend(this, configuration);\n\tthis.initialize.apply(this, arguments);\n};\n\nhelpers.extend(Element.prototype, {\n\n\tinitialize: function() {\n\t\tthis.hidden = false;\n\t},\n\n\tpivot: function() {\n\t\tvar me = this;\n\t\tif (!me._view) {\n\t\t\tme._view = helpers.clone(me._model);\n\t\t}\n\t\tme._start = {};\n\t\treturn me;\n\t},\n\n\ttransition: function(ease) {\n\t\tvar me = this;\n\t\tvar model = me._model;\n\t\tvar start = me._start;\n\t\tvar view = me._view;\n\n\t\t// No animation -> No Transition\n\t\tif (!model || ease === 1) {\n\t\t\tme._view = model;\n\t\t\tme._start = null;\n\t\t\treturn me;\n\t\t}\n\n\t\tif (!view) {\n\t\t\tview = me._view = {};\n\t\t}\n\n\t\tif (!start) {\n\t\t\tstart = me._start = {};\n\t\t}\n\n\t\tinterpolate(start, view, model, ease);\n\n\t\treturn me;\n\t},\n\n\ttooltipPosition: function() {\n\t\treturn {\n\t\t\tx: this._model.x,\n\t\t\ty: this._model.y\n\t\t};\n\t},\n\n\thasValue: function() {\n\t\treturn helpers.isNumber(this._model.x) && helpers.isNumber(this._model.y);\n\t}\n});\n\nElement.extend = helpers.inherits;\n\nmodule.exports = Element;\n\n},{\"3\":3,\"45\":45}],27:[function(require,module,exports){\n/* global window: false */\n/* global document: false */\n'use strict';\n\nvar color = require(3);\nvar defaults = require(25);\nvar helpers = require(45);\n\nmodule.exports = function(Chart) {\n\n\t// -- Basic js utility methods\n\n\thelpers.configMerge = function(/* objects ... */) {\n\t\treturn helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), {\n\t\t\tmerger: function(key, target, source, options) {\n\t\t\t\tvar tval = target[key] || {};\n\t\t\t\tvar sval = source[key];\n\n\t\t\t\tif (key === 'scales') {\n\t\t\t\t\t// scale config merging is complex. Add our own function here for that\n\t\t\t\t\ttarget[key] = helpers.scaleMerge(tval, sval);\n\t\t\t\t} else if (key === 'scale') {\n\t\t\t\t\t// used in polar area & radar charts since there is only one scale\n\t\t\t\t\ttarget[key] = helpers.merge(tval, [Chart.scaleService.getScaleDefaults(sval.type), sval]);\n\t\t\t\t} else {\n\t\t\t\t\thelpers._merger(key, target, source, options);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t};\n\n\thelpers.scaleMerge = function(/* objects ... */) {\n\t\treturn helpers.merge(helpers.clone(arguments[0]), [].slice.call(arguments, 1), {\n\t\t\tmerger: function(key, target, source, options) {\n\t\t\t\tif (key === 'xAxes' || key === 'yAxes') {\n\t\t\t\t\tvar slen = source[key].length;\n\t\t\t\t\tvar i, type, scale;\n\n\t\t\t\t\tif (!target[key]) {\n\t\t\t\t\t\ttarget[key] = [];\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (i = 0; i < slen; ++i) {\n\t\t\t\t\t\tscale = source[key][i];\n\t\t\t\t\t\ttype = helpers.valueOrDefault(scale.type, key === 'xAxes' ? 'category' : 'linear');\n\n\t\t\t\t\t\tif (i >= target[key].length) {\n\t\t\t\t\t\t\ttarget[key].push({});\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (!target[key][i].type || (scale.type && scale.type !== target[key][i].type)) {\n\t\t\t\t\t\t\t// new/untyped scale or type changed: let's apply the new defaults\n\t\t\t\t\t\t\t// then merge source scale to correctly overwrite the defaults.\n\t\t\t\t\t\t\thelpers.merge(target[key][i], [Chart.scaleService.getScaleDefaults(type), scale]);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// scales type are the same\n\t\t\t\t\t\t\thelpers.merge(target[key][i], scale);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\thelpers._merger(key, target, source, options);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t};\n\n\thelpers.where = function(collection, filterCallback) {\n\t\tif (helpers.isArray(collection) && Array.prototype.filter) {\n\t\t\treturn collection.filter(filterCallback);\n\t\t}\n\t\tvar filtered = [];\n\n\t\thelpers.each(collection, function(item) {\n\t\t\tif (filterCallback(item)) {\n\t\t\t\tfiltered.push(item);\n\t\t\t}\n\t\t});\n\n\t\treturn filtered;\n\t};\n\thelpers.findIndex = Array.prototype.findIndex ?\n\t\tfunction(array, callback, scope) {\n\t\t\treturn array.findIndex(callback, scope);\n\t\t} :\n\t\tfunction(array, callback, scope) {\n\t\t\tscope = scope === undefined ? array : scope;\n\t\t\tfor (var i = 0, ilen = array.length; i < ilen; ++i) {\n\t\t\t\tif (callback.call(scope, array[i], i, array)) {\n\t\t\t\t\treturn i;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn -1;\n\t\t};\n\thelpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex) {\n\t\t// Default to start of the array\n\t\tif (helpers.isNullOrUndef(startIndex)) {\n\t\t\tstartIndex = -1;\n\t\t}\n\t\tfor (var i = startIndex + 1; i < arrayToSearch.length; i++) {\n\t\t\tvar currentItem = arrayToSearch[i];\n\t\t\tif (filterCallback(currentItem)) {\n\t\t\t\treturn currentItem;\n\t\t\t}\n\t\t}\n\t};\n\thelpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex) {\n\t\t// Default to end of the array\n\t\tif (helpers.isNullOrUndef(startIndex)) {\n\t\t\tstartIndex = arrayToSearch.length;\n\t\t}\n\t\tfor (var i = startIndex - 1; i >= 0; i--) {\n\t\t\tvar currentItem = arrayToSearch[i];\n\t\t\tif (filterCallback(currentItem)) {\n\t\t\t\treturn currentItem;\n\t\t\t}\n\t\t}\n\t};\n\n\t// -- Math methods\n\thelpers.isNumber = function(n) {\n\t\treturn !isNaN(parseFloat(n)) && isFinite(n);\n\t};\n\thelpers.almostEquals = function(x, y, epsilon) {\n\t\treturn Math.abs(x - y) < epsilon;\n\t};\n\thelpers.almostWhole = function(x, epsilon) {\n\t\tvar rounded = Math.round(x);\n\t\treturn (((rounded - epsilon) < x) && ((rounded + epsilon) > x));\n\t};\n\thelpers.max = function(array) {\n\t\treturn array.reduce(function(max, value) {\n\t\t\tif (!isNaN(value)) {\n\t\t\t\treturn Math.max(max, value);\n\t\t\t}\n\t\t\treturn max;\n\t\t}, Number.NEGATIVE_INFINITY);\n\t};\n\thelpers.min = function(array) {\n\t\treturn array.reduce(function(min, value) {\n\t\t\tif (!isNaN(value)) {\n\t\t\t\treturn Math.min(min, value);\n\t\t\t}\n\t\t\treturn min;\n\t\t}, Number.POSITIVE_INFINITY);\n\t};\n\thelpers.sign = Math.sign ?\n\t\tfunction(x) {\n\t\t\treturn Math.sign(x);\n\t\t} :\n\t\tfunction(x) {\n\t\t\tx = +x; // convert to a number\n\t\t\tif (x === 0 || isNaN(x)) {\n\t\t\t\treturn x;\n\t\t\t}\n\t\t\treturn x > 0 ? 1 : -1;\n\t\t};\n\thelpers.log10 = Math.log10 ?\n\t\tfunction(x) {\n\t\t\treturn Math.log10(x);\n\t\t} :\n\t\tfunction(x) {\n\t\t\tvar exponent = Math.log(x) * Math.LOG10E; // Math.LOG10E = 1 / Math.LN10.\n\t\t\t// Check for whole powers of 10,\n\t\t\t// which due to floating point rounding error should be corrected.\n\t\t\tvar powerOf10 = Math.round(exponent);\n\t\t\tvar isPowerOf10 = x === Math.pow(10, powerOf10);\n\n\t\t\treturn isPowerOf10 ? powerOf10 : exponent;\n\t\t};\n\thelpers.toRadians = function(degrees) {\n\t\treturn degrees * (Math.PI / 180);\n\t};\n\thelpers.toDegrees = function(radians) {\n\t\treturn radians * (180 / Math.PI);\n\t};\n\t// Gets the angle from vertical upright to the point about a centre.\n\thelpers.getAngleFromPoint = function(centrePoint, anglePoint) {\n\t\tvar distanceFromXCenter = anglePoint.x - centrePoint.x;\n\t\tvar distanceFromYCenter = anglePoint.y - centrePoint.y;\n\t\tvar radialDistanceFromCenter = Math.sqrt(distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);\n\n\t\tvar angle = Math.atan2(distanceFromYCenter, distanceFromXCenter);\n\n\t\tif (angle < (-0.5 * Math.PI)) {\n\t\t\tangle += 2.0 * Math.PI; // make sure the returned angle is in the range of (-PI/2, 3PI/2]\n\t\t}\n\n\t\treturn {\n\t\t\tangle: angle,\n\t\t\tdistance: radialDistanceFromCenter\n\t\t};\n\t};\n\thelpers.distanceBetweenPoints = function(pt1, pt2) {\n\t\treturn Math.sqrt(Math.pow(pt2.x - pt1.x, 2) + Math.pow(pt2.y - pt1.y, 2));\n\t};\n\thelpers.aliasPixel = function(pixelWidth) {\n\t\treturn (pixelWidth % 2 === 0) ? 0 : 0.5;\n\t};\n\thelpers.splineCurve = function(firstPoint, middlePoint, afterPoint, t) {\n\t\t// Props to Rob Spencer at scaled innovation for his post on splining between points\n\t\t// http://scaledinnovation.com/analytics/splines/aboutSplines.html\n\n\t\t// This function must also respect \"skipped\" points\n\n\t\tvar previous = firstPoint.skip ? middlePoint : firstPoint;\n\t\tvar current = middlePoint;\n\t\tvar next = afterPoint.skip ? middlePoint : afterPoint;\n\n\t\tvar d01 = Math.sqrt(Math.pow(current.x - previous.x, 2) + Math.pow(current.y - previous.y, 2));\n\t\tvar d12 = Math.sqrt(Math.pow(next.x - current.x, 2) + Math.pow(next.y - current.y, 2));\n\n\t\tvar s01 = d01 / (d01 + d12);\n\t\tvar s12 = d12 / (d01 + d12);\n\n\t\t// If all points are the same, s01 & s02 will be inf\n\t\ts01 = isNaN(s01) ? 0 : s01;\n\t\ts12 = isNaN(s12) ? 0 : s12;\n\n\t\tvar fa = t * s01; // scaling factor for triangle Ta\n\t\tvar fb = t * s12;\n\n\t\treturn {\n\t\t\tprevious: {\n\t\t\t\tx: current.x - fa * (next.x - previous.x),\n\t\t\t\ty: current.y - fa * (next.y - previous.y)\n\t\t\t},\n\t\t\tnext: {\n\t\t\t\tx: current.x + fb * (next.x - previous.x),\n\t\t\t\ty: current.y + fb * (next.y - previous.y)\n\t\t\t}\n\t\t};\n\t};\n\thelpers.EPSILON = Number.EPSILON || 1e-14;\n\thelpers.splineCurveMonotone = function(points) {\n\t\t// This function calculates Bézier control points in a similar way than |splineCurve|,\n\t\t// but preserves monotonicity of the provided data and ensures no local extremums are added\n\t\t// between the dataset discrete points due to the interpolation.\n\t\t// See : https://en.wikipedia.org/wiki/Monotone_cubic_interpolation\n\n\t\tvar pointsWithTangents = (points || []).map(function(point) {\n\t\t\treturn {\n\t\t\t\tmodel: point._model,\n\t\t\t\tdeltaK: 0,\n\t\t\t\tmK: 0\n\t\t\t};\n\t\t});\n\n\t\t// Calculate slopes (deltaK) and initialize tangents (mK)\n\t\tvar pointsLen = pointsWithTangents.length;\n\t\tvar i, pointBefore, pointCurrent, pointAfter;\n\t\tfor (i = 0; i < pointsLen; ++i) {\n\t\t\tpointCurrent = pointsWithTangents[i];\n\t\t\tif (pointCurrent.model.skip) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tpointBefore = i > 0 ? pointsWithTangents[i - 1] : null;\n\t\t\tpointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;\n\t\t\tif (pointAfter && !pointAfter.model.skip) {\n\t\t\t\tvar slopeDeltaX = (pointAfter.model.x - pointCurrent.model.x);\n\n\t\t\t\t// In the case of two points that appear at the same x pixel, slopeDeltaX is 0\n\t\t\t\tpointCurrent.deltaK = slopeDeltaX !== 0 ? (pointAfter.model.y - pointCurrent.model.y) / slopeDeltaX : 0;\n\t\t\t}\n\n\t\t\tif (!pointBefore || pointBefore.model.skip) {\n\t\t\t\tpointCurrent.mK = pointCurrent.deltaK;\n\t\t\t} else if (!pointAfter || pointAfter.model.skip) {\n\t\t\t\tpointCurrent.mK = pointBefore.deltaK;\n\t\t\t} else if (this.sign(pointBefore.deltaK) !== this.sign(pointCurrent.deltaK)) {\n\t\t\t\tpointCurrent.mK = 0;\n\t\t\t} else {\n\t\t\t\tpointCurrent.mK = (pointBefore.deltaK + pointCurrent.deltaK) / 2;\n\t\t\t}\n\t\t}\n\n\t\t// Adjust tangents to ensure monotonic properties\n\t\tvar alphaK, betaK, tauK, squaredMagnitude;\n\t\tfor (i = 0; i < pointsLen - 1; ++i) {\n\t\t\tpointCurrent = pointsWithTangents[i];\n\t\t\tpointAfter = pointsWithTangents[i + 1];\n\t\t\tif (pointCurrent.model.skip || pointAfter.model.skip) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (helpers.almostEquals(pointCurrent.deltaK, 0, this.EPSILON)) {\n\t\t\t\tpointCurrent.mK = pointAfter.mK = 0;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\talphaK = pointCurrent.mK / pointCurrent.deltaK;\n\t\t\tbetaK = pointAfter.mK / pointCurrent.deltaK;\n\t\t\tsquaredMagnitude = Math.pow(alphaK, 2) + Math.pow(betaK, 2);\n\t\t\tif (squaredMagnitude <= 9) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\ttauK = 3 / Math.sqrt(squaredMagnitude);\n\t\t\tpointCurrent.mK = alphaK * tauK * pointCurrent.deltaK;\n\t\t\tpointAfter.mK = betaK * tauK * pointCurrent.deltaK;\n\t\t}\n\n\t\t// Compute control points\n\t\tvar deltaX;\n\t\tfor (i = 0; i < pointsLen; ++i) {\n\t\t\tpointCurrent = pointsWithTangents[i];\n\t\t\tif (pointCurrent.model.skip) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tpointBefore = i > 0 ? pointsWithTangents[i - 1] : null;\n\t\t\tpointAfter = i < pointsLen - 1 ? pointsWithTangents[i + 1] : null;\n\t\t\tif (pointBefore && !pointBefore.model.skip) {\n\t\t\t\tdeltaX = (pointCurrent.model.x - pointBefore.model.x) / 3;\n\t\t\t\tpointCurrent.model.controlPointPreviousX = pointCurrent.model.x - deltaX;\n\t\t\t\tpointCurrent.model.controlPointPreviousY = pointCurrent.model.y - deltaX * pointCurrent.mK;\n\t\t\t}\n\t\t\tif (pointAfter && !pointAfter.model.skip) {\n\t\t\t\tdeltaX = (pointAfter.model.x - pointCurrent.model.x) / 3;\n\t\t\t\tpointCurrent.model.controlPointNextX = pointCurrent.model.x + deltaX;\n\t\t\t\tpointCurrent.model.controlPointNextY = pointCurrent.model.y + deltaX * pointCurrent.mK;\n\t\t\t}\n\t\t}\n\t};\n\thelpers.nextItem = function(collection, index, loop) {\n\t\tif (loop) {\n\t\t\treturn index >= collection.length - 1 ? collection[0] : collection[index + 1];\n\t\t}\n\t\treturn index >= collection.length - 1 ? collection[collection.length - 1] : collection[index + 1];\n\t};\n\thelpers.previousItem = function(collection, index, loop) {\n\t\tif (loop) {\n\t\t\treturn index <= 0 ? collection[collection.length - 1] : collection[index - 1];\n\t\t}\n\t\treturn index <= 0 ? collection[0] : collection[index - 1];\n\t};\n\t// Implementation of the nice number algorithm used in determining where axis labels will go\n\thelpers.niceNum = function(range, round) {\n\t\tvar exponent = Math.floor(helpers.log10(range));\n\t\tvar fraction = range / Math.pow(10, exponent);\n\t\tvar niceFraction;\n\n\t\tif (round) {\n\t\t\tif (fraction < 1.5) {\n\t\t\t\tniceFraction = 1;\n\t\t\t} else if (fraction < 3) {\n\t\t\t\tniceFraction = 2;\n\t\t\t} else if (fraction < 7) {\n\t\t\t\tniceFraction = 5;\n\t\t\t} else {\n\t\t\t\tniceFraction = 10;\n\t\t\t}\n\t\t} else if (fraction <= 1.0) {\n\t\t\tniceFraction = 1;\n\t\t} else if (fraction <= 2) {\n\t\t\tniceFraction = 2;\n\t\t} else if (fraction <= 5) {\n\t\t\tniceFraction = 5;\n\t\t} else {\n\t\t\tniceFraction = 10;\n\t\t}\n\n\t\treturn niceFraction * Math.pow(10, exponent);\n\t};\n\t// Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/\n\thelpers.requestAnimFrame = (function() {\n\t\tif (typeof window === 'undefined') {\n\t\t\treturn function(callback) {\n\t\t\t\tcallback();\n\t\t\t};\n\t\t}\n\t\treturn window.requestAnimationFrame ||\n\t\t\twindow.webkitRequestAnimationFrame ||\n\t\t\twindow.mozRequestAnimationFrame ||\n\t\t\twindow.oRequestAnimationFrame ||\n\t\t\twindow.msRequestAnimationFrame ||\n\t\t\tfunction(callback) {\n\t\t\t\treturn window.setTimeout(callback, 1000 / 60);\n\t\t\t};\n\t}());\n\t// -- DOM methods\n\thelpers.getRelativePosition = function(evt, chart) {\n\t\tvar mouseX, mouseY;\n\t\tvar e = evt.originalEvent || evt;\n\t\tvar canvas = evt.currentTarget || evt.srcElement;\n\t\tvar boundingRect = canvas.getBoundingClientRect();\n\n\t\tvar touches = e.touches;\n\t\tif (touches && touches.length > 0) {\n\t\t\tmouseX = touches[0].clientX;\n\t\t\tmouseY = touches[0].clientY;\n\n\t\t} else {\n\t\t\tmouseX = e.clientX;\n\t\t\tmouseY = e.clientY;\n\t\t}\n\n\t\t// Scale mouse coordinates into canvas coordinates\n\t\t// by following the pattern laid out by 'jerryj' in the comments of\n\t\t// http://www.html5canvastutorials.com/advanced/html5-canvas-mouse-coordinates/\n\t\tvar paddingLeft = parseFloat(helpers.getStyle(canvas, 'padding-left'));\n\t\tvar paddingTop = parseFloat(helpers.getStyle(canvas, 'padding-top'));\n\t\tvar paddingRight = parseFloat(helpers.getStyle(canvas, 'padding-right'));\n\t\tvar paddingBottom = parseFloat(helpers.getStyle(canvas, 'padding-bottom'));\n\t\tvar width = boundingRect.right - boundingRect.left - paddingLeft - paddingRight;\n\t\tvar height = boundingRect.bottom - boundingRect.top - paddingTop - paddingBottom;\n\n\t\t// We divide by the current device pixel ratio, because the canvas is scaled up by that amount in each direction. However\n\t\t// the backend model is in unscaled coordinates. Since we are going to deal with our model coordinates, we go back here\n\t\tmouseX = Math.round((mouseX - boundingRect.left - paddingLeft) / (width) * canvas.width / chart.currentDevicePixelRatio);\n\t\tmouseY = Math.round((mouseY - boundingRect.top - paddingTop) / (height) * canvas.height / chart.currentDevicePixelRatio);\n\n\t\treturn {\n\t\t\tx: mouseX,\n\t\t\ty: mouseY\n\t\t};\n\n\t};\n\n\t// Private helper function to convert max-width/max-height values that may be percentages into a number\n\tfunction parseMaxStyle(styleValue, node, parentProperty) {\n\t\tvar valueInPixels;\n\t\tif (typeof styleValue === 'string') {\n\t\t\tvalueInPixels = parseInt(styleValue, 10);\n\n\t\t\tif (styleValue.indexOf('%') !== -1) {\n\t\t\t\t// percentage * size in dimension\n\t\t\t\tvalueInPixels = valueInPixels / 100 * node.parentNode[parentProperty];\n\t\t\t}\n\t\t} else {\n\t\t\tvalueInPixels = styleValue;\n\t\t}\n\n\t\treturn valueInPixels;\n\t}\n\n\t/**\n\t * Returns if the given value contains an effective constraint.\n\t * @private\n\t */\n\tfunction isConstrainedValue(value) {\n\t\treturn value !== undefined && value !== null && value !== 'none';\n\t}\n\n\t// Private helper to get a constraint dimension\n\t// @param domNode : the node to check the constraint on\n\t// @param maxStyle : the style that defines the maximum for the direction we are using (maxWidth / maxHeight)\n\t// @param percentageProperty : property of parent to use when calculating width as a percentage\n\t// @see http://www.nathanaeljones.com/blog/2013/reading-max-width-cross-browser\n\tfunction getConstraintDimension(domNode, maxStyle, percentageProperty) {\n\t\tvar view = document.defaultView;\n\t\tvar parentNode = domNode.parentNode;\n\t\tvar constrainedNode = view.getComputedStyle(domNode)[maxStyle];\n\t\tvar constrainedContainer = view.getComputedStyle(parentNode)[maxStyle];\n\t\tvar hasCNode = isConstrainedValue(constrainedNode);\n\t\tvar hasCContainer = isConstrainedValue(constrainedContainer);\n\t\tvar infinity = Number.POSITIVE_INFINITY;\n\n\t\tif (hasCNode || hasCContainer) {\n\t\t\treturn Math.min(\n\t\t\t\thasCNode ? parseMaxStyle(constrainedNode, domNode, percentageProperty) : infinity,\n\t\t\t\thasCContainer ? parseMaxStyle(constrainedContainer, parentNode, percentageProperty) : infinity);\n\t\t}\n\n\t\treturn 'none';\n\t}\n\t// returns Number or undefined if no constraint\n\thelpers.getConstraintWidth = function(domNode) {\n\t\treturn getConstraintDimension(domNode, 'max-width', 'clientWidth');\n\t};\n\t// returns Number or undefined if no constraint\n\thelpers.getConstraintHeight = function(domNode) {\n\t\treturn getConstraintDimension(domNode, 'max-height', 'clientHeight');\n\t};\n\thelpers.getMaximumWidth = function(domNode) {\n\t\tvar container = domNode.parentNode;\n\t\tif (!container) {\n\t\t\treturn domNode.clientWidth;\n\t\t}\n\n\t\tvar paddingLeft = parseInt(helpers.getStyle(container, 'padding-left'), 10);\n\t\tvar paddingRight = parseInt(helpers.getStyle(container, 'padding-right'), 10);\n\t\tvar w = container.clientWidth - paddingLeft - paddingRight;\n\t\tvar cw = helpers.getConstraintWidth(domNode);\n\t\treturn isNaN(cw) ? w : Math.min(w, cw);\n\t};\n\thelpers.getMaximumHeight = function(domNode) {\n\t\tvar container = domNode.parentNode;\n\t\tif (!container) {\n\t\t\treturn domNode.clientHeight;\n\t\t}\n\n\t\tvar paddingTop = parseInt(helpers.getStyle(container, 'padding-top'), 10);\n\t\tvar paddingBottom = parseInt(helpers.getStyle(container, 'padding-bottom'), 10);\n\t\tvar h = container.clientHeight - paddingTop - paddingBottom;\n\t\tvar ch = helpers.getConstraintHeight(domNode);\n\t\treturn isNaN(ch) ? h : Math.min(h, ch);\n\t};\n\thelpers.getStyle = function(el, property) {\n\t\treturn el.currentStyle ?\n\t\t\tel.currentStyle[property] :\n\t\t\tdocument.defaultView.getComputedStyle(el, null).getPropertyValue(property);\n\t};\n\thelpers.retinaScale = function(chart, forceRatio) {\n\t\tvar pixelRatio = chart.currentDevicePixelRatio = forceRatio || window.devicePixelRatio || 1;\n\t\tif (pixelRatio === 1) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar canvas = chart.canvas;\n\t\tvar height = chart.height;\n\t\tvar width = chart.width;\n\n\t\tcanvas.height = height * pixelRatio;\n\t\tcanvas.width = width * pixelRatio;\n\t\tchart.ctx.scale(pixelRatio, pixelRatio);\n\n\t\t// If no style has been set on the canvas, the render size is used as display size,\n\t\t// making the chart visually bigger, so let's enforce it to the \"correct\" values.\n\t\t// See https://github.com/chartjs/Chart.js/issues/3575\n\t\tif (!canvas.style.height && !canvas.style.width) {\n\t\t\tcanvas.style.height = height + 'px';\n\t\t\tcanvas.style.width = width + 'px';\n\t\t}\n\t};\n\t// -- Canvas methods\n\thelpers.fontString = function(pixelSize, fontStyle, fontFamily) {\n\t\treturn fontStyle + ' ' + pixelSize + 'px ' + fontFamily;\n\t};\n\thelpers.longestText = function(ctx, font, arrayOfThings, cache) {\n\t\tcache = cache || {};\n\t\tvar data = cache.data = cache.data || {};\n\t\tvar gc = cache.garbageCollect = cache.garbageCollect || [];\n\n\t\tif (cache.font !== font) {\n\t\t\tdata = cache.data = {};\n\t\t\tgc = cache.garbageCollect = [];\n\t\t\tcache.font = font;\n\t\t}\n\n\t\tctx.font = font;\n\t\tvar longest = 0;\n\t\thelpers.each(arrayOfThings, function(thing) {\n\t\t\t// Undefined strings and arrays should not be measured\n\t\t\tif (thing !== undefined && thing !== null && helpers.isArray(thing) !== true) {\n\t\t\t\tlongest = helpers.measureText(ctx, data, gc, longest, thing);\n\t\t\t} else if (helpers.isArray(thing)) {\n\t\t\t\t// if it is an array lets measure each element\n\t\t\t\t// to do maybe simplify this function a bit so we can do this more recursively?\n\t\t\t\thelpers.each(thing, function(nestedThing) {\n\t\t\t\t\t// Undefined strings and arrays should not be measured\n\t\t\t\t\tif (nestedThing !== undefined && nestedThing !== null && !helpers.isArray(nestedThing)) {\n\t\t\t\t\t\tlongest = helpers.measureText(ctx, data, gc, longest, nestedThing);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\t\t});\n\n\t\tvar gcLen = gc.length / 2;\n\t\tif (gcLen > arrayOfThings.length) {\n\t\t\tfor (var i = 0; i < gcLen; i++) {\n\t\t\t\tdelete data[gc[i]];\n\t\t\t}\n\t\t\tgc.splice(0, gcLen);\n\t\t}\n\t\treturn longest;\n\t};\n\thelpers.measureText = function(ctx, data, gc, longest, string) {\n\t\tvar textWidth = data[string];\n\t\tif (!textWidth) {\n\t\t\ttextWidth = data[string] = ctx.measureText(string).width;\n\t\t\tgc.push(string);\n\t\t}\n\t\tif (textWidth > longest) {\n\t\t\tlongest = textWidth;\n\t\t}\n\t\treturn longest;\n\t};\n\thelpers.numberOfLabelLines = function(arrayOfThings) {\n\t\tvar numberOfLines = 1;\n\t\thelpers.each(arrayOfThings, function(thing) {\n\t\t\tif (helpers.isArray(thing)) {\n\t\t\t\tif (thing.length > numberOfLines) {\n\t\t\t\t\tnumberOfLines = thing.length;\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\treturn numberOfLines;\n\t};\n\n\thelpers.color = !color ?\n\t\tfunction(value) {\n\t\t\tconsole.error('Color.js not found!');\n\t\t\treturn value;\n\t\t} :\n\t\tfunction(value) {\n\t\t\t/* global CanvasGradient */\n\t\t\tif (value instanceof CanvasGradient) {\n\t\t\t\tvalue = defaults.global.defaultColor;\n\t\t\t}\n\n\t\t\treturn color(value);\n\t\t};\n\n\thelpers.getHoverColor = function(colorValue) {\n\t\t/* global CanvasPattern */\n\t\treturn (colorValue instanceof CanvasPattern) ?\n\t\t\tcolorValue :\n\t\t\thelpers.color(colorValue).saturate(0.5).darken(0.1).rgbString();\n\t};\n};\n\n},{\"25\":25,\"3\":3,\"45\":45}],28:[function(require,module,exports){\n'use strict';\n\nvar helpers = require(45);\n\n/**\n * Helper function to get relative position for an event\n * @param {Event|IEvent} event - The event to get the position for\n * @param {Chart} chart - The chart\n * @returns {Point} the event position\n */\nfunction getRelativePosition(e, chart) {\n\tif (e.native) {\n\t\treturn {\n\t\t\tx: e.x,\n\t\t\ty: e.y\n\t\t};\n\t}\n\n\treturn helpers.getRelativePosition(e, chart);\n}\n\n/**\n * Helper function to traverse all of the visible elements in the chart\n * @param chart {chart} the chart\n * @param handler {Function} the callback to execute for each visible item\n */\nfunction parseVisibleItems(chart, handler) {\n\tvar datasets = chart.data.datasets;\n\tvar meta, i, j, ilen, jlen;\n\n\tfor (i = 0, ilen = datasets.length; i < ilen; ++i) {\n\t\tif (!chart.isDatasetVisible(i)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tmeta = chart.getDatasetMeta(i);\n\t\tfor (j = 0, jlen = meta.data.length; j < jlen; ++j) {\n\t\t\tvar element = meta.data[j];\n\t\t\tif (!element._view.skip) {\n\t\t\t\thandler(element);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Helper function to get the items that intersect the event position\n * @param items {ChartElement[]} elements to filter\n * @param position {Point} the point to be nearest to\n * @return {ChartElement[]} the nearest items\n */\nfunction getIntersectItems(chart, position) {\n\tvar elements = [];\n\n\tparseVisibleItems(chart, function(element) {\n\t\tif (element.inRange(position.x, position.y)) {\n\t\t\telements.push(element);\n\t\t}\n\t});\n\n\treturn elements;\n}\n\n/**\n * Helper function to get the items nearest to the event position considering all visible items in teh chart\n * @param chart {Chart} the chart to look at elements from\n * @param position {Point} the point to be nearest to\n * @param intersect {Boolean} if true, only consider items that intersect the position\n * @param distanceMetric {Function} function to provide the distance between points\n * @return {ChartElement[]} the nearest items\n */\nfunction getNearestItems(chart, position, intersect, distanceMetric) {\n\tvar minDistance = Number.POSITIVE_INFINITY;\n\tvar nearestItems = [];\n\n\tparseVisibleItems(chart, function(element) {\n\t\tif (intersect && !element.inRange(position.x, position.y)) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar center = element.getCenterPoint();\n\t\tvar distance = distanceMetric(position, center);\n\n\t\tif (distance < minDistance) {\n\t\t\tnearestItems = [element];\n\t\t\tminDistance = distance;\n\t\t} else if (distance === minDistance) {\n\t\t\t// Can have multiple items at the same distance in which case we sort by size\n\t\t\tnearestItems.push(element);\n\t\t}\n\t});\n\n\treturn nearestItems;\n}\n\n/**\n * Get a distance metric function for two points based on the\n * axis mode setting\n * @param {String} axis the axis mode. x|y|xy\n */\nfunction getDistanceMetricForAxis(axis) {\n\tvar useX = axis.indexOf('x') !== -1;\n\tvar useY = axis.indexOf('y') !== -1;\n\n\treturn function(pt1, pt2) {\n\t\tvar deltaX = useX ? Math.abs(pt1.x - pt2.x) : 0;\n\t\tvar deltaY = useY ? Math.abs(pt1.y - pt2.y) : 0;\n\t\treturn Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2));\n\t};\n}\n\nfunction indexMode(chart, e, options) {\n\tvar position = getRelativePosition(e, chart);\n\t// Default axis for index mode is 'x' to match old behaviour\n\toptions.axis = options.axis || 'x';\n\tvar distanceMetric = getDistanceMetricForAxis(options.axis);\n\tvar items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);\n\tvar elements = [];\n\n\tif (!items.length) {\n\t\treturn [];\n\t}\n\n\tchart.data.datasets.forEach(function(dataset, datasetIndex) {\n\t\tif (chart.isDatasetVisible(datasetIndex)) {\n\t\t\tvar meta = chart.getDatasetMeta(datasetIndex);\n\t\t\tvar element = meta.data[items[0]._index];\n\n\t\t\t// don't count items that are skipped (null data)\n\t\t\tif (element && !element._view.skip) {\n\t\t\t\telements.push(element);\n\t\t\t}\n\t\t}\n\t});\n\n\treturn elements;\n}\n\n/**\n * @interface IInteractionOptions\n */\n/**\n * If true, only consider items that intersect the point\n * @name IInterfaceOptions#boolean\n * @type Boolean\n */\n\n/**\n * Contains interaction related functions\n * @namespace Chart.Interaction\n */\nmodule.exports = {\n\t// Helper function for different modes\n\tmodes: {\n\t\tsingle: function(chart, e) {\n\t\t\tvar position = getRelativePosition(e, chart);\n\t\t\tvar elements = [];\n\n\t\t\tparseVisibleItems(chart, function(element) {\n\t\t\t\tif (element.inRange(position.x, position.y)) {\n\t\t\t\t\telements.push(element);\n\t\t\t\t\treturn elements;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn elements.slice(0, 1);\n\t\t},\n\n\t\t/**\n\t\t * @function Chart.Interaction.modes.label\n\t\t * @deprecated since version 2.4.0\n\t\t * @todo remove at version 3\n\t\t * @private\n\t\t */\n\t\tlabel: indexMode,\n\n\t\t/**\n\t\t * Returns items at the same index. If the options.intersect parameter is true, we only return items if we intersect something\n\t\t * If the options.intersect mode is false, we find the nearest item and return the items at the same index as that item\n\t\t * @function Chart.Interaction.modes.index\n\t\t * @since v2.4.0\n\t\t * @param chart {chart} the chart we are returning items from\n\t\t * @param e {Event} the event we are find things at\n\t\t * @param options {IInteractionOptions} options to use during interaction\n\t\t * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned\n\t\t */\n\t\tindex: indexMode,\n\n\t\t/**\n\t\t * Returns items in the same dataset. If the options.intersect parameter is true, we only return items if we intersect something\n\t\t * If the options.intersect is false, we find the nearest item and return the items in that dataset\n\t\t * @function Chart.Interaction.modes.dataset\n\t\t * @param chart {chart} the chart we are returning items from\n\t\t * @param e {Event} the event we are find things at\n\t\t * @param options {IInteractionOptions} options to use during interaction\n\t\t * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned\n\t\t */\n\t\tdataset: function(chart, e, options) {\n\t\t\tvar position = getRelativePosition(e, chart);\n\t\t\toptions.axis = options.axis || 'xy';\n\t\t\tvar distanceMetric = getDistanceMetricForAxis(options.axis);\n\t\t\tvar items = options.intersect ? getIntersectItems(chart, position) : getNearestItems(chart, position, false, distanceMetric);\n\n\t\t\tif (items.length > 0) {\n\t\t\t\titems = chart.getDatasetMeta(items[0]._datasetIndex).data;\n\t\t\t}\n\n\t\t\treturn items;\n\t\t},\n\n\t\t/**\n\t\t * @function Chart.Interaction.modes.x-axis\n\t\t * @deprecated since version 2.4.0. Use index mode and intersect == true\n\t\t * @todo remove at version 3\n\t\t * @private\n\t\t */\n\t\t'x-axis': function(chart, e) {\n\t\t\treturn indexMode(chart, e, {intersect: false});\n\t\t},\n\n\t\t/**\n\t\t * Point mode returns all elements that hit test based on the event position\n\t\t * of the event\n\t\t * @function Chart.Interaction.modes.intersect\n\t\t * @param chart {chart} the chart we are returning items from\n\t\t * @param e {Event} the event we are find things at\n\t\t * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned\n\t\t */\n\t\tpoint: function(chart, e) {\n\t\t\tvar position = getRelativePosition(e, chart);\n\t\t\treturn getIntersectItems(chart, position);\n\t\t},\n\n\t\t/**\n\t\t * nearest mode returns the element closest to the point\n\t\t * @function Chart.Interaction.modes.intersect\n\t\t * @param chart {chart} the chart we are returning items from\n\t\t * @param e {Event} the event we are find things at\n\t\t * @param options {IInteractionOptions} options to use\n\t\t * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned\n\t\t */\n\t\tnearest: function(chart, e, options) {\n\t\t\tvar position = getRelativePosition(e, chart);\n\t\t\toptions.axis = options.axis || 'xy';\n\t\t\tvar distanceMetric = getDistanceMetricForAxis(options.axis);\n\t\t\tvar nearestItems = getNearestItems(chart, position, options.intersect, distanceMetric);\n\n\t\t\t// We have multiple items at the same distance from the event. Now sort by smallest\n\t\t\tif (nearestItems.length > 1) {\n\t\t\t\tnearestItems.sort(function(a, b) {\n\t\t\t\t\tvar sizeA = a.getArea();\n\t\t\t\t\tvar sizeB = b.getArea();\n\t\t\t\t\tvar ret = sizeA - sizeB;\n\n\t\t\t\t\tif (ret === 0) {\n\t\t\t\t\t\t// if equal sort by dataset index\n\t\t\t\t\t\tret = a._datasetIndex - b._datasetIndex;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn ret;\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Return only 1 item\n\t\t\treturn nearestItems.slice(0, 1);\n\t\t},\n\n\t\t/**\n\t\t * x mode returns the elements that hit-test at the current x coordinate\n\t\t * @function Chart.Interaction.modes.x\n\t\t * @param chart {chart} the chart we are returning items from\n\t\t * @param e {Event} the event we are find things at\n\t\t * @param options {IInteractionOptions} options to use\n\t\t * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned\n\t\t */\n\t\tx: function(chart, e, options) {\n\t\t\tvar position = getRelativePosition(e, chart);\n\t\t\tvar items = [];\n\t\t\tvar intersectsItem = false;\n\n\t\t\tparseVisibleItems(chart, function(element) {\n\t\t\t\tif (element.inXRange(position.x)) {\n\t\t\t\t\titems.push(element);\n\t\t\t\t}\n\n\t\t\t\tif (element.inRange(position.x, position.y)) {\n\t\t\t\t\tintersectsItem = true;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// If we want to trigger on an intersect and we don't have any items\n\t\t\t// that intersect the position, return nothing\n\t\t\tif (options.intersect && !intersectsItem) {\n\t\t\t\titems = [];\n\t\t\t}\n\t\t\treturn items;\n\t\t},\n\n\t\t/**\n\t\t * y mode returns the elements that hit-test at the current y coordinate\n\t\t * @function Chart.Interaction.modes.y\n\t\t * @param chart {chart} the chart we are returning items from\n\t\t * @param e {Event} the event we are find things at\n\t\t * @param options {IInteractionOptions} options to use\n\t\t * @return {Chart.Element[]} Array of elements that are under the point. If none are found, an empty array is returned\n\t\t */\n\t\ty: function(chart, e, options) {\n\t\t\tvar position = getRelativePosition(e, chart);\n\t\t\tvar items = [];\n\t\t\tvar intersectsItem = false;\n\n\t\t\tparseVisibleItems(chart, function(element) {\n\t\t\t\tif (element.inYRange(position.y)) {\n\t\t\t\t\titems.push(element);\n\t\t\t\t}\n\n\t\t\t\tif (element.inRange(position.x, position.y)) {\n\t\t\t\t\tintersectsItem = true;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\t// If we want to trigger on an intersect and we don't have any items\n\t\t\t// that intersect the position, return nothing\n\t\t\tif (options.intersect && !intersectsItem) {\n\t\t\t\titems = [];\n\t\t\t}\n\t\t\treturn items;\n\t\t}\n\t}\n};\n\n},{\"45\":45}],29:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\n\ndefaults._set('global', {\n\tresponsive: true,\n\tresponsiveAnimationDuration: 0,\n\tmaintainAspectRatio: true,\n\tevents: ['mousemove', 'mouseout', 'click', 'touchstart', 'touchmove'],\n\thover: {\n\t\tonHover: null,\n\t\tmode: 'nearest',\n\t\tintersect: true,\n\t\tanimationDuration: 400\n\t},\n\tonClick: null,\n\tdefaultColor: 'rgba(0,0,0,0.1)',\n\tdefaultFontColor: '#666',\n\tdefaultFontFamily: \"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif\",\n\tdefaultFontSize: 12,\n\tdefaultFontStyle: 'normal',\n\tshowLines: true,\n\n\t// Element defaults defined in element extensions\n\telements: {},\n\n\t// Layout options such as padding\n\tlayout: {\n\t\tpadding: {\n\t\t\ttop: 0,\n\t\t\tright: 0,\n\t\t\tbottom: 0,\n\t\t\tleft: 0\n\t\t}\n\t}\n});\n\nmodule.exports = function() {\n\n\t// Occupy the global variable of Chart, and create a simple base class\n\tvar Chart = function(item, config) {\n\t\tthis.construct(item, config);\n\t\treturn this;\n\t};\n\n\tChart.Chart = Chart;\n\n\treturn Chart;\n};\n\n},{\"25\":25}],30:[function(require,module,exports){\n'use strict';\n\nvar helpers = require(45);\n\nfunction filterByPosition(array, position) {\n\treturn helpers.where(array, function(v) {\n\t\treturn v.position === position;\n\t});\n}\n\nfunction sortByWeight(array, reverse) {\n\tarray.forEach(function(v, i) {\n\t\tv._tmpIndex_ = i;\n\t\treturn v;\n\t});\n\tarray.sort(function(a, b) {\n\t\tvar v0 = reverse ? b : a;\n\t\tvar v1 = reverse ? a : b;\n\t\treturn v0.weight === v1.weight ?\n\t\t\tv0._tmpIndex_ - v1._tmpIndex_ :\n\t\t\tv0.weight - v1.weight;\n\t});\n\tarray.forEach(function(v) {\n\t\tdelete v._tmpIndex_;\n\t});\n}\n\n/**\n * @interface ILayoutItem\n * @prop {String} position - The position of the item in the chart layout. Possible values are\n * 'left', 'top', 'right', 'bottom', and 'chartArea'\n * @prop {Number} weight - The weight used to sort the item. Higher weights are further away from the chart area\n * @prop {Boolean} fullWidth - if true, and the item is horizontal, then push vertical boxes down\n * @prop {Function} isHorizontal - returns true if the layout item is horizontal (ie. top or bottom)\n * @prop {Function} update - Takes two parameters: width and height. Returns size of item\n * @prop {Function} getPadding -  Returns an object with padding on the edges\n * @prop {Number} width - Width of item. Must be valid after update()\n * @prop {Number} height - Height of item. Must be valid after update()\n * @prop {Number} left - Left edge of the item. Set by layout system and cannot be used in update\n * @prop {Number} top - Top edge of the item. Set by layout system and cannot be used in update\n * @prop {Number} right - Right edge of the item. Set by layout system and cannot be used in update\n * @prop {Number} bottom - Bottom edge of the item. Set by layout system and cannot be used in update\n */\n\n// The layout service is very self explanatory.  It's responsible for the layout within a chart.\n// Scales, Legends and Plugins all rely on the layout service and can easily register to be placed anywhere they need\n// It is this service's responsibility of carrying out that layout.\nmodule.exports = {\n\tdefaults: {},\n\n\t/**\n\t * Register a box to a chart.\n\t * A box is simply a reference to an object that requires layout. eg. Scales, Legend, Title.\n\t * @param {Chart} chart - the chart to use\n\t * @param {ILayoutItem} item - the item to add to be layed out\n\t */\n\taddBox: function(chart, item) {\n\t\tif (!chart.boxes) {\n\t\t\tchart.boxes = [];\n\t\t}\n\n\t\t// initialize item with default values\n\t\titem.fullWidth = item.fullWidth || false;\n\t\titem.position = item.position || 'top';\n\t\titem.weight = item.weight || 0;\n\n\t\tchart.boxes.push(item);\n\t},\n\n\t/**\n\t * Remove a layoutItem from a chart\n\t * @param {Chart} chart - the chart to remove the box from\n\t * @param {Object} layoutItem - the item to remove from the layout\n\t */\n\tremoveBox: function(chart, layoutItem) {\n\t\tvar index = chart.boxes ? chart.boxes.indexOf(layoutItem) : -1;\n\t\tif (index !== -1) {\n\t\t\tchart.boxes.splice(index, 1);\n\t\t}\n\t},\n\n\t/**\n\t * Sets (or updates) options on the given `item`.\n\t * @param {Chart} chart - the chart in which the item lives (or will be added to)\n\t * @param {Object} item - the item to configure with the given options\n\t * @param {Object} options - the new item options.\n\t */\n\tconfigure: function(chart, item, options) {\n\t\tvar props = ['fullWidth', 'position', 'weight'];\n\t\tvar ilen = props.length;\n\t\tvar i = 0;\n\t\tvar prop;\n\n\t\tfor (; i < ilen; ++i) {\n\t\t\tprop = props[i];\n\t\t\tif (options.hasOwnProperty(prop)) {\n\t\t\t\titem[prop] = options[prop];\n\t\t\t}\n\t\t}\n\t},\n\n\t/**\n\t * Fits boxes of the given chart into the given size by having each box measure itself\n\t * then running a fitting algorithm\n\t * @param {Chart} chart - the chart\n\t * @param {Number} width - the width to fit into\n\t * @param {Number} height - the height to fit into\n\t */\n\tupdate: function(chart, width, height) {\n\t\tif (!chart) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar layoutOptions = chart.options.layout || {};\n\t\tvar padding = helpers.options.toPadding(layoutOptions.padding);\n\t\tvar leftPadding = padding.left;\n\t\tvar rightPadding = padding.right;\n\t\tvar topPadding = padding.top;\n\t\tvar bottomPadding = padding.bottom;\n\n\t\tvar leftBoxes = filterByPosition(chart.boxes, 'left');\n\t\tvar rightBoxes = filterByPosition(chart.boxes, 'right');\n\t\tvar topBoxes = filterByPosition(chart.boxes, 'top');\n\t\tvar bottomBoxes = filterByPosition(chart.boxes, 'bottom');\n\t\tvar chartAreaBoxes = filterByPosition(chart.boxes, 'chartArea');\n\n\t\t// Sort boxes by weight. A higher weight is further away from the chart area\n\t\tsortByWeight(leftBoxes, true);\n\t\tsortByWeight(rightBoxes, false);\n\t\tsortByWeight(topBoxes, true);\n\t\tsortByWeight(bottomBoxes, false);\n\n\t\t// Essentially we now have any number of boxes on each of the 4 sides.\n\t\t// Our canvas looks like the following.\n\t\t// The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and\n\t\t// B1 is the bottom axis\n\t\t// There are also 4 quadrant-like locations (left to right instead of clockwise) reserved for chart overlays\n\t\t// These locations are single-box locations only, when trying to register a chartArea location that is already taken,\n\t\t// an error will be thrown.\n\t\t//\n\t\t// |----------------------------------------------------|\n\t\t// |                  T1 (Full Width)                   |\n\t\t// |----------------------------------------------------|\n\t\t// |    |    |                 T2                  |    |\n\t\t// |    |----|-------------------------------------|----|\n\t\t// |    |    | C1 |                           | C2 |    |\n\t\t// |    |    |----|                           |----|    |\n\t\t// |    |    |                                     |    |\n\t\t// | L1 | L2 |           ChartArea (C0)            | R1 |\n\t\t// |    |    |                                     |    |\n\t\t// |    |    |----|                           |----|    |\n\t\t// |    |    | C3 |                           | C4 |    |\n\t\t// |    |----|-------------------------------------|----|\n\t\t// |    |    |                 B1                  |    |\n\t\t// |----------------------------------------------------|\n\t\t// |                  B2 (Full Width)                   |\n\t\t// |----------------------------------------------------|\n\t\t//\n\t\t// What we do to find the best sizing, we do the following\n\t\t// 1. Determine the minimum size of the chart area.\n\t\t// 2. Split the remaining width equally between each vertical axis\n\t\t// 3. Split the remaining height equally between each horizontal axis\n\t\t// 4. Give each layout the maximum size it can be. The layout will return it's minimum size\n\t\t// 5. Adjust the sizes of each axis based on it's minimum reported size.\n\t\t// 6. Refit each axis\n\t\t// 7. Position each axis in the final location\n\t\t// 8. Tell the chart the final location of the chart area\n\t\t// 9. Tell any axes that overlay the chart area the positions of the chart area\n\n\t\t// Step 1\n\t\tvar chartWidth = width - leftPadding - rightPadding;\n\t\tvar chartHeight = height - topPadding - bottomPadding;\n\t\tvar chartAreaWidth = chartWidth / 2; // min 50%\n\t\tvar chartAreaHeight = chartHeight / 2; // min 50%\n\n\t\t// Step 2\n\t\tvar verticalBoxWidth = (width - chartAreaWidth) / (leftBoxes.length + rightBoxes.length);\n\n\t\t// Step 3\n\t\tvar horizontalBoxHeight = (height - chartAreaHeight) / (topBoxes.length + bottomBoxes.length);\n\n\t\t// Step 4\n\t\tvar maxChartAreaWidth = chartWidth;\n\t\tvar maxChartAreaHeight = chartHeight;\n\t\tvar minBoxSizes = [];\n\n\t\tfunction getMinimumBoxSize(box) {\n\t\t\tvar minSize;\n\t\t\tvar isHorizontal = box.isHorizontal();\n\n\t\t\tif (isHorizontal) {\n\t\t\t\tminSize = box.update(box.fullWidth ? chartWidth : maxChartAreaWidth, horizontalBoxHeight);\n\t\t\t\tmaxChartAreaHeight -= minSize.height;\n\t\t\t} else {\n\t\t\t\tminSize = box.update(verticalBoxWidth, maxChartAreaHeight);\n\t\t\t\tmaxChartAreaWidth -= minSize.width;\n\t\t\t}\n\n\t\t\tminBoxSizes.push({\n\t\t\t\thorizontal: isHorizontal,\n\t\t\t\tminSize: minSize,\n\t\t\t\tbox: box,\n\t\t\t});\n\t\t}\n\n\t\thelpers.each(leftBoxes.concat(rightBoxes, topBoxes, bottomBoxes), getMinimumBoxSize);\n\n\t\t// If a horizontal box has padding, we move the left boxes over to avoid ugly charts (see issue #2478)\n\t\tvar maxHorizontalLeftPadding = 0;\n\t\tvar maxHorizontalRightPadding = 0;\n\t\tvar maxVerticalTopPadding = 0;\n\t\tvar maxVerticalBottomPadding = 0;\n\n\t\thelpers.each(topBoxes.concat(bottomBoxes), function(horizontalBox) {\n\t\t\tif (horizontalBox.getPadding) {\n\t\t\t\tvar boxPadding = horizontalBox.getPadding();\n\t\t\t\tmaxHorizontalLeftPadding = Math.max(maxHorizontalLeftPadding, boxPadding.left);\n\t\t\t\tmaxHorizontalRightPadding = Math.max(maxHorizontalRightPadding, boxPadding.right);\n\t\t\t}\n\t\t});\n\n\t\thelpers.each(leftBoxes.concat(rightBoxes), function(verticalBox) {\n\t\t\tif (verticalBox.getPadding) {\n\t\t\t\tvar boxPadding = verticalBox.getPadding();\n\t\t\t\tmaxVerticalTopPadding = Math.max(maxVerticalTopPadding, boxPadding.top);\n\t\t\t\tmaxVerticalBottomPadding = Math.max(maxVerticalBottomPadding, boxPadding.bottom);\n\t\t\t}\n\t\t});\n\n\t\t// At this point, maxChartAreaHeight and maxChartAreaWidth are the size the chart area could\n\t\t// be if the axes are drawn at their minimum sizes.\n\t\t// Steps 5 & 6\n\t\tvar totalLeftBoxesWidth = leftPadding;\n\t\tvar totalRightBoxesWidth = rightPadding;\n\t\tvar totalTopBoxesHeight = topPadding;\n\t\tvar totalBottomBoxesHeight = bottomPadding;\n\n\t\t// Function to fit a box\n\t\tfunction fitBox(box) {\n\t\t\tvar minBoxSize = helpers.findNextWhere(minBoxSizes, function(minBox) {\n\t\t\t\treturn minBox.box === box;\n\t\t\t});\n\n\t\t\tif (minBoxSize) {\n\t\t\t\tif (box.isHorizontal()) {\n\t\t\t\t\tvar scaleMargin = {\n\t\t\t\t\t\tleft: Math.max(totalLeftBoxesWidth, maxHorizontalLeftPadding),\n\t\t\t\t\t\tright: Math.max(totalRightBoxesWidth, maxHorizontalRightPadding),\n\t\t\t\t\t\ttop: 0,\n\t\t\t\t\t\tbottom: 0\n\t\t\t\t\t};\n\n\t\t\t\t\t// Don't use min size here because of label rotation. When the labels are rotated, their rotation highly depends\n\t\t\t\t\t// on the margin. Sometimes they need to increase in size slightly\n\t\t\t\t\tbox.update(box.fullWidth ? chartWidth : maxChartAreaWidth, chartHeight / 2, scaleMargin);\n\t\t\t\t} else {\n\t\t\t\t\tbox.update(minBoxSize.minSize.width, maxChartAreaHeight);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Update, and calculate the left and right margins for the horizontal boxes\n\t\thelpers.each(leftBoxes.concat(rightBoxes), fitBox);\n\n\t\thelpers.each(leftBoxes, function(box) {\n\t\t\ttotalLeftBoxesWidth += box.width;\n\t\t});\n\n\t\thelpers.each(rightBoxes, function(box) {\n\t\t\ttotalRightBoxesWidth += box.width;\n\t\t});\n\n\t\t// Set the Left and Right margins for the horizontal boxes\n\t\thelpers.each(topBoxes.concat(bottomBoxes), fitBox);\n\n\t\t// Figure out how much margin is on the top and bottom of the vertical boxes\n\t\thelpers.each(topBoxes, function(box) {\n\t\t\ttotalTopBoxesHeight += box.height;\n\t\t});\n\n\t\thelpers.each(bottomBoxes, function(box) {\n\t\t\ttotalBottomBoxesHeight += box.height;\n\t\t});\n\n\t\tfunction finalFitVerticalBox(box) {\n\t\t\tvar minBoxSize = helpers.findNextWhere(minBoxSizes, function(minSize) {\n\t\t\t\treturn minSize.box === box;\n\t\t\t});\n\n\t\t\tvar scaleMargin = {\n\t\t\t\tleft: 0,\n\t\t\t\tright: 0,\n\t\t\t\ttop: totalTopBoxesHeight,\n\t\t\t\tbottom: totalBottomBoxesHeight\n\t\t\t};\n\n\t\t\tif (minBoxSize) {\n\t\t\t\tbox.update(minBoxSize.minSize.width, maxChartAreaHeight, scaleMargin);\n\t\t\t}\n\t\t}\n\n\t\t// Let the left layout know the final margin\n\t\thelpers.each(leftBoxes.concat(rightBoxes), finalFitVerticalBox);\n\n\t\t// Recalculate because the size of each layout might have changed slightly due to the margins (label rotation for instance)\n\t\ttotalLeftBoxesWidth = leftPadding;\n\t\ttotalRightBoxesWidth = rightPadding;\n\t\ttotalTopBoxesHeight = topPadding;\n\t\ttotalBottomBoxesHeight = bottomPadding;\n\n\t\thelpers.each(leftBoxes, function(box) {\n\t\t\ttotalLeftBoxesWidth += box.width;\n\t\t});\n\n\t\thelpers.each(rightBoxes, function(box) {\n\t\t\ttotalRightBoxesWidth += box.width;\n\t\t});\n\n\t\thelpers.each(topBoxes, function(box) {\n\t\t\ttotalTopBoxesHeight += box.height;\n\t\t});\n\t\thelpers.each(bottomBoxes, function(box) {\n\t\t\ttotalBottomBoxesHeight += box.height;\n\t\t});\n\n\t\t// We may be adding some padding to account for rotated x axis labels\n\t\tvar leftPaddingAddition = Math.max(maxHorizontalLeftPadding - totalLeftBoxesWidth, 0);\n\t\ttotalLeftBoxesWidth += leftPaddingAddition;\n\t\ttotalRightBoxesWidth += Math.max(maxHorizontalRightPadding - totalRightBoxesWidth, 0);\n\n\t\tvar topPaddingAddition = Math.max(maxVerticalTopPadding - totalTopBoxesHeight, 0);\n\t\ttotalTopBoxesHeight += topPaddingAddition;\n\t\ttotalBottomBoxesHeight += Math.max(maxVerticalBottomPadding - totalBottomBoxesHeight, 0);\n\n\t\t// Figure out if our chart area changed. This would occur if the dataset layout label rotation\n\t\t// changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do\n\t\t// without calling `fit` again\n\t\tvar newMaxChartAreaHeight = height - totalTopBoxesHeight - totalBottomBoxesHeight;\n\t\tvar newMaxChartAreaWidth = width - totalLeftBoxesWidth - totalRightBoxesWidth;\n\n\t\tif (newMaxChartAreaWidth !== maxChartAreaWidth || newMaxChartAreaHeight !== maxChartAreaHeight) {\n\t\t\thelpers.each(leftBoxes, function(box) {\n\t\t\t\tbox.height = newMaxChartAreaHeight;\n\t\t\t});\n\n\t\t\thelpers.each(rightBoxes, function(box) {\n\t\t\t\tbox.height = newMaxChartAreaHeight;\n\t\t\t});\n\n\t\t\thelpers.each(topBoxes, function(box) {\n\t\t\t\tif (!box.fullWidth) {\n\t\t\t\t\tbox.width = newMaxChartAreaWidth;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\thelpers.each(bottomBoxes, function(box) {\n\t\t\t\tif (!box.fullWidth) {\n\t\t\t\t\tbox.width = newMaxChartAreaWidth;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tmaxChartAreaHeight = newMaxChartAreaHeight;\n\t\t\tmaxChartAreaWidth = newMaxChartAreaWidth;\n\t\t}\n\n\t\t// Step 7 - Position the boxes\n\t\tvar left = leftPadding + leftPaddingAddition;\n\t\tvar top = topPadding + topPaddingAddition;\n\n\t\tfunction placeBox(box) {\n\t\t\tif (box.isHorizontal()) {\n\t\t\t\tbox.left = box.fullWidth ? leftPadding : totalLeftBoxesWidth;\n\t\t\t\tbox.right = box.fullWidth ? width - rightPadding : totalLeftBoxesWidth + maxChartAreaWidth;\n\t\t\t\tbox.top = top;\n\t\t\t\tbox.bottom = top + box.height;\n\n\t\t\t\t// Move to next point\n\t\t\t\ttop = box.bottom;\n\n\t\t\t} else {\n\n\t\t\t\tbox.left = left;\n\t\t\t\tbox.right = left + box.width;\n\t\t\t\tbox.top = totalTopBoxesHeight;\n\t\t\t\tbox.bottom = totalTopBoxesHeight + maxChartAreaHeight;\n\n\t\t\t\t// Move to next point\n\t\t\t\tleft = box.right;\n\t\t\t}\n\t\t}\n\n\t\thelpers.each(leftBoxes.concat(topBoxes), placeBox);\n\n\t\t// Account for chart width and height\n\t\tleft += maxChartAreaWidth;\n\t\ttop += maxChartAreaHeight;\n\n\t\thelpers.each(rightBoxes, placeBox);\n\t\thelpers.each(bottomBoxes, placeBox);\n\n\t\t// Step 8\n\t\tchart.chartArea = {\n\t\t\tleft: totalLeftBoxesWidth,\n\t\t\ttop: totalTopBoxesHeight,\n\t\t\tright: totalLeftBoxesWidth + maxChartAreaWidth,\n\t\t\tbottom: totalTopBoxesHeight + maxChartAreaHeight\n\t\t};\n\n\t\t// Step 9\n\t\thelpers.each(chartAreaBoxes, function(box) {\n\t\t\tbox.left = chart.chartArea.left;\n\t\t\tbox.top = chart.chartArea.top;\n\t\t\tbox.right = chart.chartArea.right;\n\t\t\tbox.bottom = chart.chartArea.bottom;\n\n\t\t\tbox.update(maxChartAreaWidth, maxChartAreaHeight);\n\t\t});\n\t}\n};\n\n},{\"45\":45}],31:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\nvar helpers = require(45);\n\ndefaults._set('global', {\n\tplugins: {}\n});\n\n/**\n * The plugin service singleton\n * @namespace Chart.plugins\n * @since 2.1.0\n */\nmodule.exports = {\n\t/**\n\t * Globally registered plugins.\n\t * @private\n\t */\n\t_plugins: [],\n\n\t/**\n\t * This identifier is used to invalidate the descriptors cache attached to each chart\n\t * when a global plugin is registered or unregistered. In this case, the cache ID is\n\t * incremented and descriptors are regenerated during following API calls.\n\t * @private\n\t */\n\t_cacheId: 0,\n\n\t/**\n\t * Registers the given plugin(s) if not already registered.\n\t * @param {Array|Object} plugins plugin instance(s).\n\t */\n\tregister: function(plugins) {\n\t\tvar p = this._plugins;\n\t\t([]).concat(plugins).forEach(function(plugin) {\n\t\t\tif (p.indexOf(plugin) === -1) {\n\t\t\t\tp.push(plugin);\n\t\t\t}\n\t\t});\n\n\t\tthis._cacheId++;\n\t},\n\n\t/**\n\t * Unregisters the given plugin(s) only if registered.\n\t * @param {Array|Object} plugins plugin instance(s).\n\t */\n\tunregister: function(plugins) {\n\t\tvar p = this._plugins;\n\t\t([]).concat(plugins).forEach(function(plugin) {\n\t\t\tvar idx = p.indexOf(plugin);\n\t\t\tif (idx !== -1) {\n\t\t\t\tp.splice(idx, 1);\n\t\t\t}\n\t\t});\n\n\t\tthis._cacheId++;\n\t},\n\n\t/**\n\t * Remove all registered plugins.\n\t * @since 2.1.5\n\t */\n\tclear: function() {\n\t\tthis._plugins = [];\n\t\tthis._cacheId++;\n\t},\n\n\t/**\n\t * Returns the number of registered plugins?\n\t * @returns {Number}\n\t * @since 2.1.5\n\t */\n\tcount: function() {\n\t\treturn this._plugins.length;\n\t},\n\n\t/**\n\t * Returns all registered plugin instances.\n\t * @returns {Array} array of plugin objects.\n\t * @since 2.1.5\n\t */\n\tgetAll: function() {\n\t\treturn this._plugins;\n\t},\n\n\t/**\n\t * Calls enabled plugins for `chart` on the specified hook and with the given args.\n\t * This method immediately returns as soon as a plugin explicitly returns false. The\n\t * returned value can be used, for instance, to interrupt the current action.\n\t * @param {Object} chart - The chart instance for which plugins should be called.\n\t * @param {String} hook - The name of the plugin method to call (e.g. 'beforeUpdate').\n\t * @param {Array} [args] - Extra arguments to apply to the hook call.\n\t * @returns {Boolean} false if any of the plugins return false, else returns true.\n\t */\n\tnotify: function(chart, hook, args) {\n\t\tvar descriptors = this.descriptors(chart);\n\t\tvar ilen = descriptors.length;\n\t\tvar i, descriptor, plugin, params, method;\n\n\t\tfor (i = 0; i < ilen; ++i) {\n\t\t\tdescriptor = descriptors[i];\n\t\t\tplugin = descriptor.plugin;\n\t\t\tmethod = plugin[hook];\n\t\t\tif (typeof method === 'function') {\n\t\t\t\tparams = [chart].concat(args || []);\n\t\t\t\tparams.push(descriptor.options);\n\t\t\t\tif (method.apply(plugin, params) === false) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t},\n\n\t/**\n\t * Returns descriptors of enabled plugins for the given chart.\n\t * @returns {Array} [{ plugin, options }]\n\t * @private\n\t */\n\tdescriptors: function(chart) {\n\t\tvar cache = chart.$plugins || (chart.$plugins = {});\n\t\tif (cache.id === this._cacheId) {\n\t\t\treturn cache.descriptors;\n\t\t}\n\n\t\tvar plugins = [];\n\t\tvar descriptors = [];\n\t\tvar config = (chart && chart.config) || {};\n\t\tvar options = (config.options && config.options.plugins) || {};\n\n\t\tthis._plugins.concat(config.plugins || []).forEach(function(plugin) {\n\t\t\tvar idx = plugins.indexOf(plugin);\n\t\t\tif (idx !== -1) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar id = plugin.id;\n\t\t\tvar opts = options[id];\n\t\t\tif (opts === false) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (opts === true) {\n\t\t\t\topts = helpers.clone(defaults.global.plugins[id]);\n\t\t\t}\n\n\t\t\tplugins.push(plugin);\n\t\t\tdescriptors.push({\n\t\t\t\tplugin: plugin,\n\t\t\t\toptions: opts || {}\n\t\t\t});\n\t\t});\n\n\t\tcache.descriptors = descriptors;\n\t\tcache.id = this._cacheId;\n\t\treturn descriptors;\n\t},\n\n\t/**\n\t * Invalidates cache for the given chart: descriptors hold a reference on plugin option,\n\t * but in some cases, this reference can be changed by the user when updating options.\n\t * https://github.com/chartjs/Chart.js/issues/5111#issuecomment-355934167\n\t * @private\n\t */\n\t_invalidate: function(chart) {\n\t\tdelete chart.$plugins;\n\t}\n};\n\n/**\n * Plugin extension hooks.\n * @interface IPlugin\n * @since 2.1.0\n */\n/**\n * @method IPlugin#beforeInit\n * @desc Called before initializing `chart`.\n * @param {Chart.Controller} chart - The chart instance.\n * @param {Object} options - The plugin options.\n */\n/**\n * @method IPlugin#afterInit\n * @desc Called after `chart` has been initialized and before the first update.\n * @param {Chart.Controller} chart - The chart instance.\n * @param {Object} options - The plugin options.\n */\n/**\n * @method IPlugin#beforeUpdate\n * @desc Called before updating `chart`. If any plugin returns `false`, the update\n * is cancelled (and thus subsequent render(s)) until another `update` is triggered.\n * @param {Chart.Controller} chart - The chart instance.\n * @param {Object} options - The plugin options.\n * @returns {Boolean} `false` to cancel the chart update.\n */\n/**\n * @method IPlugin#afterUpdate\n * @desc Called after `chart` has been updated and before rendering. Note that this\n * hook will not be called if the chart update has been previously cancelled.\n * @param {Chart.Controller} chart - The chart instance.\n * @param {Object} options - The plugin options.\n */\n/**\n * @method IPlugin#beforeDatasetsUpdate\n * @desc Called before updating the `chart` datasets. If any plugin returns `false`,\n * the datasets update is cancelled until another `update` is triggered.\n * @param {Chart.Controller} chart - The chart instance.\n * @param {Object} options - The plugin options.\n * @returns {Boolean} false to cancel the datasets update.\n * @since version 2.1.5\n*/\n/**\n * @method IPlugin#afterDatasetsUpdate\n * @desc Called after the `chart` datasets have been updated. Note that this hook\n * will not be called if the datasets update has been previously cancelled.\n * @param {Chart.Controller} chart - The chart instance.\n * @param {Object} options - The plugin options.\n * @since version 2.1.5\n */\n/**\n * @method IPlugin#beforeDatasetUpdate\n * @desc Called before updating the `chart` dataset at the given `args.index`. If any plugin\n * returns `false`, the datasets update is cancelled until another `update` is triggered.\n * @param {Chart} chart - The chart instance.\n * @param {Object} args - The call arguments.\n * @param {Number} args.index - The dataset index.\n * @param {Object} args.meta - The dataset metadata.\n * @param {Object} options - The plugin options.\n * @returns {Boolean} `false` to cancel the chart datasets drawing.\n */\n/**\n * @method IPlugin#afterDatasetUpdate\n * @desc Called after the `chart` datasets at the given `args.index` has been updated. Note\n * that this hook will not be called if the datasets update has been previously cancelled.\n * @param {Chart} chart - The chart instance.\n * @param {Object} args - The call arguments.\n * @param {Number} args.index - The dataset index.\n * @param {Object} args.meta - The dataset metadata.\n * @param {Object} options - The plugin options.\n */\n/**\n * @method IPlugin#beforeLayout\n * @desc Called before laying out `chart`. If any plugin returns `false`,\n * the layout update is cancelled until another `update` is triggered.\n * @param {Chart.Controller} chart - The chart instance.\n * @param {Object} options - The plugin options.\n * @returns {Boolean} `false` to cancel the chart layout.\n */\n/**\n * @method IPlugin#afterLayout\n * @desc Called after the `chart` has been layed out. Note that this hook will not\n * be called if the layout update has been previously cancelled.\n * @param {Chart.Controller} chart - The chart instance.\n * @param {Object} options - The plugin options.\n */\n/**\n * @method IPlugin#beforeRender\n * @desc Called before rendering `chart`. If any plugin returns `false`,\n * the rendering is cancelled until another `render` is triggered.\n * @param {Chart.Controller} chart - The chart instance.\n * @param {Object} options - The plugin options.\n * @returns {Boolean} `false` to cancel the chart rendering.\n */\n/**\n * @method IPlugin#afterRender\n * @desc Called after the `chart` has been fully rendered (and animation completed). Note\n * that this hook will not be called if the rendering has been previously cancelled.\n * @param {Chart.Controller} chart - The chart instance.\n * @param {Object} options - The plugin options.\n */\n/**\n * @method IPlugin#beforeDraw\n * @desc Called before drawing `chart` at every animation frame specified by the given\n * easing value. If any plugin returns `false`, the frame drawing is cancelled until\n * another `render` is triggered.\n * @param {Chart.Controller} chart - The chart instance.\n * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.\n * @param {Object} options - The plugin options.\n * @returns {Boolean} `false` to cancel the chart drawing.\n */\n/**\n * @method IPlugin#afterDraw\n * @desc Called after the `chart` has been drawn for the specific easing value. Note\n * that this hook will not be called if the drawing has been previously cancelled.\n * @param {Chart.Controller} chart - The chart instance.\n * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.\n * @param {Object} options - The plugin options.\n */\n/**\n * @method IPlugin#beforeDatasetsDraw\n * @desc Called before drawing the `chart` datasets. If any plugin returns `false`,\n * the datasets drawing is cancelled until another `render` is triggered.\n * @param {Chart.Controller} chart - The chart instance.\n * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.\n * @param {Object} options - The plugin options.\n * @returns {Boolean} `false` to cancel the chart datasets drawing.\n */\n/**\n * @method IPlugin#afterDatasetsDraw\n * @desc Called after the `chart` datasets have been drawn. Note that this hook\n * will not be called if the datasets drawing has been previously cancelled.\n * @param {Chart.Controller} chart - The chart instance.\n * @param {Number} easingValue - The current animation value, between 0.0 and 1.0.\n * @param {Object} options - The plugin options.\n */\n/**\n * @method IPlugin#beforeDatasetDraw\n * @desc Called before drawing the `chart` dataset at the given `args.index` (datasets\n * are drawn in the reverse order). If any plugin returns `false`, the datasets drawing\n * is cancelled until another `render` is triggered.\n * @param {Chart} chart - The chart instance.\n * @param {Object} args - The call arguments.\n * @param {Number} args.index - The dataset index.\n * @param {Object} args.meta - The dataset metadata.\n * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.\n * @param {Object} options - The plugin options.\n * @returns {Boolean} `false` to cancel the chart datasets drawing.\n */\n/**\n * @method IPlugin#afterDatasetDraw\n * @desc Called after the `chart` datasets at the given `args.index` have been drawn\n * (datasets are drawn in the reverse order). Note that this hook will not be called\n * if the datasets drawing has been previously cancelled.\n * @param {Chart} chart - The chart instance.\n * @param {Object} args - The call arguments.\n * @param {Number} args.index - The dataset index.\n * @param {Object} args.meta - The dataset metadata.\n * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.\n * @param {Object} options - The plugin options.\n */\n/**\n * @method IPlugin#beforeTooltipDraw\n * @desc Called before drawing the `tooltip`. If any plugin returns `false`,\n * the tooltip drawing is cancelled until another `render` is triggered.\n * @param {Chart} chart - The chart instance.\n * @param {Object} args - The call arguments.\n * @param {Object} args.tooltip - The tooltip.\n * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.\n * @param {Object} options - The plugin options.\n * @returns {Boolean} `false` to cancel the chart tooltip drawing.\n */\n/**\n * @method IPlugin#afterTooltipDraw\n * @desc Called after drawing the `tooltip`. Note that this hook will not\n * be called if the tooltip drawing has been previously cancelled.\n * @param {Chart} chart - The chart instance.\n * @param {Object} args - The call arguments.\n * @param {Object} args.tooltip - The tooltip.\n * @param {Number} args.easingValue - The current animation value, between 0.0 and 1.0.\n * @param {Object} options - The plugin options.\n */\n/**\n * @method IPlugin#beforeEvent\n * @desc Called before processing the specified `event`. If any plugin returns `false`,\n * the event will be discarded.\n * @param {Chart.Controller} chart - The chart instance.\n * @param {IEvent} event - The event object.\n * @param {Object} options - The plugin options.\n */\n/**\n * @method IPlugin#afterEvent\n * @desc Called after the `event` has been consumed. Note that this hook\n * will not be called if the `event` has been previously discarded.\n * @param {Chart.Controller} chart - The chart instance.\n * @param {IEvent} event - The event object.\n * @param {Object} options - The plugin options.\n */\n/**\n * @method IPlugin#resize\n * @desc Called after the chart as been resized.\n * @param {Chart.Controller} chart - The chart instance.\n * @param {Number} size - The new canvas display size (eq. canvas.style width & height).\n * @param {Object} options - The plugin options.\n */\n/**\n * @method IPlugin#destroy\n * @desc Called after the chart as been destroyed.\n * @param {Chart.Controller} chart - The chart instance.\n * @param {Object} options - The plugin options.\n */\n\n},{\"25\":25,\"45\":45}],32:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\nvar Element = require(26);\nvar helpers = require(45);\nvar Ticks = require(34);\n\ndefaults._set('scale', {\n\tdisplay: true,\n\tposition: 'left',\n\toffset: false,\n\n\t// grid line settings\n\tgridLines: {\n\t\tdisplay: true,\n\t\tcolor: 'rgba(0, 0, 0, 0.1)',\n\t\tlineWidth: 1,\n\t\tdrawBorder: true,\n\t\tdrawOnChartArea: true,\n\t\tdrawTicks: true,\n\t\ttickMarkLength: 10,\n\t\tzeroLineWidth: 1,\n\t\tzeroLineColor: 'rgba(0,0,0,0.25)',\n\t\tzeroLineBorderDash: [],\n\t\tzeroLineBorderDashOffset: 0.0,\n\t\toffsetGridLines: false,\n\t\tborderDash: [],\n\t\tborderDashOffset: 0.0\n\t},\n\n\t// scale label\n\tscaleLabel: {\n\t\t// display property\n\t\tdisplay: false,\n\n\t\t// actual label\n\t\tlabelString: '',\n\n\t\t// line height\n\t\tlineHeight: 1.2,\n\n\t\t// top/bottom padding\n\t\tpadding: {\n\t\t\ttop: 4,\n\t\t\tbottom: 4\n\t\t}\n\t},\n\n\t// label settings\n\tticks: {\n\t\tbeginAtZero: false,\n\t\tminRotation: 0,\n\t\tmaxRotation: 50,\n\t\tmirror: false,\n\t\tpadding: 0,\n\t\treverse: false,\n\t\tdisplay: true,\n\t\tautoSkip: true,\n\t\tautoSkipPadding: 0,\n\t\tlabelOffset: 0,\n\t\t// We pass through arrays to be rendered as multiline labels, we convert Others to strings here.\n\t\tcallback: Ticks.formatters.values,\n\t\tminor: {},\n\t\tmajor: {}\n\t}\n});\n\nfunction labelsFromTicks(ticks) {\n\tvar labels = [];\n\tvar i, ilen;\n\n\tfor (i = 0, ilen = ticks.length; i < ilen; ++i) {\n\t\tlabels.push(ticks[i].label);\n\t}\n\n\treturn labels;\n}\n\nfunction getLineValue(scale, index, offsetGridLines) {\n\tvar lineValue = scale.getPixelForTick(index);\n\n\tif (offsetGridLines) {\n\t\tif (index === 0) {\n\t\t\tlineValue -= (scale.getPixelForTick(1) - lineValue) / 2;\n\t\t} else {\n\t\t\tlineValue -= (lineValue - scale.getPixelForTick(index - 1)) / 2;\n\t\t}\n\t}\n\treturn lineValue;\n}\n\nmodule.exports = function(Chart) {\n\n\tfunction computeTextSize(context, tick, font) {\n\t\treturn helpers.isArray(tick) ?\n\t\t\thelpers.longestText(context, font, tick) :\n\t\t\tcontext.measureText(tick).width;\n\t}\n\n\tfunction parseFontOptions(options) {\n\t\tvar valueOrDefault = helpers.valueOrDefault;\n\t\tvar globalDefaults = defaults.global;\n\t\tvar size = valueOrDefault(options.fontSize, globalDefaults.defaultFontSize);\n\t\tvar style = valueOrDefault(options.fontStyle, globalDefaults.defaultFontStyle);\n\t\tvar family = valueOrDefault(options.fontFamily, globalDefaults.defaultFontFamily);\n\n\t\treturn {\n\t\t\tsize: size,\n\t\t\tstyle: style,\n\t\t\tfamily: family,\n\t\t\tfont: helpers.fontString(size, style, family)\n\t\t};\n\t}\n\n\tfunction parseLineHeight(options) {\n\t\treturn helpers.options.toLineHeight(\n\t\t\thelpers.valueOrDefault(options.lineHeight, 1.2),\n\t\t\thelpers.valueOrDefault(options.fontSize, defaults.global.defaultFontSize));\n\t}\n\n\tChart.Scale = Element.extend({\n\t\t/**\n\t\t * Get the padding needed for the scale\n\t\t * @method getPadding\n\t\t * @private\n\t\t * @returns {Padding} the necessary padding\n\t\t */\n\t\tgetPadding: function() {\n\t\t\tvar me = this;\n\t\t\treturn {\n\t\t\t\tleft: me.paddingLeft || 0,\n\t\t\t\ttop: me.paddingTop || 0,\n\t\t\t\tright: me.paddingRight || 0,\n\t\t\t\tbottom: me.paddingBottom || 0\n\t\t\t};\n\t\t},\n\n\t\t/**\n\t\t * Returns the scale tick objects ({label, major})\n\t\t * @since 2.7\n\t\t */\n\t\tgetTicks: function() {\n\t\t\treturn this._ticks;\n\t\t},\n\n\t\t// These methods are ordered by lifecyle. Utilities then follow.\n\t\t// Any function defined here is inherited by all scale types.\n\t\t// Any function can be extended by the scale type\n\n\t\tmergeTicksOptions: function() {\n\t\t\tvar ticks = this.options.ticks;\n\t\t\tif (ticks.minor === false) {\n\t\t\t\tticks.minor = {\n\t\t\t\t\tdisplay: false\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (ticks.major === false) {\n\t\t\t\tticks.major = {\n\t\t\t\t\tdisplay: false\n\t\t\t\t};\n\t\t\t}\n\t\t\tfor (var key in ticks) {\n\t\t\t\tif (key !== 'major' && key !== 'minor') {\n\t\t\t\t\tif (typeof ticks.minor[key] === 'undefined') {\n\t\t\t\t\t\tticks.minor[key] = ticks[key];\n\t\t\t\t\t}\n\t\t\t\t\tif (typeof ticks.major[key] === 'undefined') {\n\t\t\t\t\t\tticks.major[key] = ticks[key];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tbeforeUpdate: function() {\n\t\t\thelpers.callback(this.options.beforeUpdate, [this]);\n\t\t},\n\t\tupdate: function(maxWidth, maxHeight, margins) {\n\t\t\tvar me = this;\n\t\t\tvar i, ilen, labels, label, ticks, tick;\n\n\t\t\t// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)\n\t\t\tme.beforeUpdate();\n\n\t\t\t// Absorb the master measurements\n\t\t\tme.maxWidth = maxWidth;\n\t\t\tme.maxHeight = maxHeight;\n\t\t\tme.margins = helpers.extend({\n\t\t\t\tleft: 0,\n\t\t\t\tright: 0,\n\t\t\t\ttop: 0,\n\t\t\t\tbottom: 0\n\t\t\t}, margins);\n\t\t\tme.longestTextCache = me.longestTextCache || {};\n\n\t\t\t// Dimensions\n\t\t\tme.beforeSetDimensions();\n\t\t\tme.setDimensions();\n\t\t\tme.afterSetDimensions();\n\n\t\t\t// Data min/max\n\t\t\tme.beforeDataLimits();\n\t\t\tme.determineDataLimits();\n\t\t\tme.afterDataLimits();\n\n\t\t\t// Ticks - `this.ticks` is now DEPRECATED!\n\t\t\t// Internal ticks are now stored as objects in the PRIVATE `this._ticks` member\n\t\t\t// and must not be accessed directly from outside this class. `this.ticks` being\n\t\t\t// around for long time and not marked as private, we can't change its structure\n\t\t\t// without unexpected breaking changes. If you need to access the scale ticks,\n\t\t\t// use scale.getTicks() instead.\n\n\t\t\tme.beforeBuildTicks();\n\n\t\t\t// New implementations should return an array of objects but for BACKWARD COMPAT,\n\t\t\t// we still support no return (`this.ticks` internally set by calling this method).\n\t\t\tticks = me.buildTicks() || [];\n\n\t\t\tme.afterBuildTicks();\n\n\t\t\tme.beforeTickToLabelConversion();\n\n\t\t\t// New implementations should return the formatted tick labels but for BACKWARD\n\t\t\t// COMPAT, we still support no return (`this.ticks` internally changed by calling\n\t\t\t// this method and supposed to contain only string values).\n\t\t\tlabels = me.convertTicksToLabels(ticks) || me.ticks;\n\n\t\t\tme.afterTickToLabelConversion();\n\n\t\t\tme.ticks = labels;   // BACKWARD COMPATIBILITY\n\n\t\t\t// IMPORTANT: from this point, we consider that `this.ticks` will NEVER change!\n\n\t\t\t// BACKWARD COMPAT: synchronize `_ticks` with labels (so potentially `this.ticks`)\n\t\t\tfor (i = 0, ilen = labels.length; i < ilen; ++i) {\n\t\t\t\tlabel = labels[i];\n\t\t\t\ttick = ticks[i];\n\t\t\t\tif (!tick) {\n\t\t\t\t\tticks.push(tick = {\n\t\t\t\t\t\tlabel: label,\n\t\t\t\t\t\tmajor: false\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\ttick.label = label;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tme._ticks = ticks;\n\n\t\t\t// Tick Rotation\n\t\t\tme.beforeCalculateTickRotation();\n\t\t\tme.calculateTickRotation();\n\t\t\tme.afterCalculateTickRotation();\n\t\t\t// Fit\n\t\t\tme.beforeFit();\n\t\t\tme.fit();\n\t\t\tme.afterFit();\n\t\t\t//\n\t\t\tme.afterUpdate();\n\n\t\t\treturn me.minSize;\n\n\t\t},\n\t\tafterUpdate: function() {\n\t\t\thelpers.callback(this.options.afterUpdate, [this]);\n\t\t},\n\n\t\t//\n\n\t\tbeforeSetDimensions: function() {\n\t\t\thelpers.callback(this.options.beforeSetDimensions, [this]);\n\t\t},\n\t\tsetDimensions: function() {\n\t\t\tvar me = this;\n\t\t\t// Set the unconstrained dimension before label rotation\n\t\t\tif (me.isHorizontal()) {\n\t\t\t\t// Reset position before calculating rotation\n\t\t\t\tme.width = me.maxWidth;\n\t\t\t\tme.left = 0;\n\t\t\t\tme.right = me.width;\n\t\t\t} else {\n\t\t\t\tme.height = me.maxHeight;\n\n\t\t\t\t// Reset position before calculating rotation\n\t\t\t\tme.top = 0;\n\t\t\t\tme.bottom = me.height;\n\t\t\t}\n\n\t\t\t// Reset padding\n\t\t\tme.paddingLeft = 0;\n\t\t\tme.paddingTop = 0;\n\t\t\tme.paddingRight = 0;\n\t\t\tme.paddingBottom = 0;\n\t\t},\n\t\tafterSetDimensions: function() {\n\t\t\thelpers.callback(this.options.afterSetDimensions, [this]);\n\t\t},\n\n\t\t// Data limits\n\t\tbeforeDataLimits: function() {\n\t\t\thelpers.callback(this.options.beforeDataLimits, [this]);\n\t\t},\n\t\tdetermineDataLimits: helpers.noop,\n\t\tafterDataLimits: function() {\n\t\t\thelpers.callback(this.options.afterDataLimits, [this]);\n\t\t},\n\n\t\t//\n\t\tbeforeBuildTicks: function() {\n\t\t\thelpers.callback(this.options.beforeBuildTicks, [this]);\n\t\t},\n\t\tbuildTicks: helpers.noop,\n\t\tafterBuildTicks: function() {\n\t\t\thelpers.callback(this.options.afterBuildTicks, [this]);\n\t\t},\n\n\t\tbeforeTickToLabelConversion: function() {\n\t\t\thelpers.callback(this.options.beforeTickToLabelConversion, [this]);\n\t\t},\n\t\tconvertTicksToLabels: function() {\n\t\t\tvar me = this;\n\t\t\t// Convert ticks to strings\n\t\t\tvar tickOpts = me.options.ticks;\n\t\t\tme.ticks = me.ticks.map(tickOpts.userCallback || tickOpts.callback, this);\n\t\t},\n\t\tafterTickToLabelConversion: function() {\n\t\t\thelpers.callback(this.options.afterTickToLabelConversion, [this]);\n\t\t},\n\n\t\t//\n\n\t\tbeforeCalculateTickRotation: function() {\n\t\t\thelpers.callback(this.options.beforeCalculateTickRotation, [this]);\n\t\t},\n\t\tcalculateTickRotation: function() {\n\t\t\tvar me = this;\n\t\t\tvar context = me.ctx;\n\t\t\tvar tickOpts = me.options.ticks;\n\t\t\tvar labels = labelsFromTicks(me._ticks);\n\n\t\t\t// Get the width of each grid by calculating the difference\n\t\t\t// between x offsets between 0 and 1.\n\t\t\tvar tickFont = parseFontOptions(tickOpts);\n\t\t\tcontext.font = tickFont.font;\n\n\t\t\tvar labelRotation = tickOpts.minRotation || 0;\n\n\t\t\tif (labels.length && me.options.display && me.isHorizontal()) {\n\t\t\t\tvar originalLabelWidth = helpers.longestText(context, tickFont.font, labels, me.longestTextCache);\n\t\t\t\tvar labelWidth = originalLabelWidth;\n\t\t\t\tvar cosRotation, sinRotation;\n\n\t\t\t\t// Allow 3 pixels x2 padding either side for label readability\n\t\t\t\tvar tickWidth = me.getPixelForTick(1) - me.getPixelForTick(0) - 6;\n\n\t\t\t\t// Max label rotation can be set or default to 90 - also act as a loop counter\n\t\t\t\twhile (labelWidth > tickWidth && labelRotation < tickOpts.maxRotation) {\n\t\t\t\t\tvar angleRadians = helpers.toRadians(labelRotation);\n\t\t\t\t\tcosRotation = Math.cos(angleRadians);\n\t\t\t\t\tsinRotation = Math.sin(angleRadians);\n\n\t\t\t\t\tif (sinRotation * originalLabelWidth > me.maxHeight) {\n\t\t\t\t\t\t// go back one step\n\t\t\t\t\t\tlabelRotation--;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\n\t\t\t\t\tlabelRotation++;\n\t\t\t\t\tlabelWidth = cosRotation * originalLabelWidth;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tme.labelRotation = labelRotation;\n\t\t},\n\t\tafterCalculateTickRotation: function() {\n\t\t\thelpers.callback(this.options.afterCalculateTickRotation, [this]);\n\t\t},\n\n\t\t//\n\n\t\tbeforeFit: function() {\n\t\t\thelpers.callback(this.options.beforeFit, [this]);\n\t\t},\n\t\tfit: function() {\n\t\t\tvar me = this;\n\t\t\t// Reset\n\t\t\tvar minSize = me.minSize = {\n\t\t\t\twidth: 0,\n\t\t\t\theight: 0\n\t\t\t};\n\n\t\t\tvar labels = labelsFromTicks(me._ticks);\n\n\t\t\tvar opts = me.options;\n\t\t\tvar tickOpts = opts.ticks;\n\t\t\tvar scaleLabelOpts = opts.scaleLabel;\n\t\t\tvar gridLineOpts = opts.gridLines;\n\t\t\tvar display = opts.display;\n\t\t\tvar isHorizontal = me.isHorizontal();\n\n\t\t\tvar tickFont = parseFontOptions(tickOpts);\n\t\t\tvar tickMarkLength = opts.gridLines.tickMarkLength;\n\n\t\t\t// Width\n\t\t\tif (isHorizontal) {\n\t\t\t\t// subtract the margins to line up with the chartArea if we are a full width scale\n\t\t\t\tminSize.width = me.isFullWidth() ? me.maxWidth - me.margins.left - me.margins.right : me.maxWidth;\n\t\t\t} else {\n\t\t\t\tminSize.width = display && gridLineOpts.drawTicks ? tickMarkLength : 0;\n\t\t\t}\n\n\t\t\t// height\n\t\t\tif (isHorizontal) {\n\t\t\t\tminSize.height = display && gridLineOpts.drawTicks ? tickMarkLength : 0;\n\t\t\t} else {\n\t\t\t\tminSize.height = me.maxHeight; // fill all the height\n\t\t\t}\n\n\t\t\t// Are we showing a title for the scale?\n\t\t\tif (scaleLabelOpts.display && display) {\n\t\t\t\tvar scaleLabelLineHeight = parseLineHeight(scaleLabelOpts);\n\t\t\t\tvar scaleLabelPadding = helpers.options.toPadding(scaleLabelOpts.padding);\n\t\t\t\tvar deltaHeight = scaleLabelLineHeight + scaleLabelPadding.height;\n\n\t\t\t\tif (isHorizontal) {\n\t\t\t\t\tminSize.height += deltaHeight;\n\t\t\t\t} else {\n\t\t\t\t\tminSize.width += deltaHeight;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Don't bother fitting the ticks if we are not showing them\n\t\t\tif (tickOpts.display && display) {\n\t\t\t\tvar largestTextWidth = helpers.longestText(me.ctx, tickFont.font, labels, me.longestTextCache);\n\t\t\t\tvar tallestLabelHeightInLines = helpers.numberOfLabelLines(labels);\n\t\t\t\tvar lineSpace = tickFont.size * 0.5;\n\t\t\t\tvar tickPadding = me.options.ticks.padding;\n\n\t\t\t\tif (isHorizontal) {\n\t\t\t\t\t// A horizontal axis is more constrained by the height.\n\t\t\t\t\tme.longestLabelWidth = largestTextWidth;\n\n\t\t\t\t\tvar angleRadians = helpers.toRadians(me.labelRotation);\n\t\t\t\t\tvar cosRotation = Math.cos(angleRadians);\n\t\t\t\t\tvar sinRotation = Math.sin(angleRadians);\n\n\t\t\t\t\t// TODO - improve this calculation\n\t\t\t\t\tvar labelHeight = (sinRotation * largestTextWidth)\n\t\t\t\t\t\t+ (tickFont.size * tallestLabelHeightInLines)\n\t\t\t\t\t\t+ (lineSpace * (tallestLabelHeightInLines - 1))\n\t\t\t\t\t\t+ lineSpace; // padding\n\n\t\t\t\t\tminSize.height = Math.min(me.maxHeight, minSize.height + labelHeight + tickPadding);\n\n\t\t\t\t\tme.ctx.font = tickFont.font;\n\t\t\t\t\tvar firstLabelWidth = computeTextSize(me.ctx, labels[0], tickFont.font);\n\t\t\t\t\tvar lastLabelWidth = computeTextSize(me.ctx, labels[labels.length - 1], tickFont.font);\n\n\t\t\t\t\t// Ensure that our ticks are always inside the canvas. When rotated, ticks are right aligned\n\t\t\t\t\t// which means that the right padding is dominated by the font height\n\t\t\t\t\tif (me.labelRotation !== 0) {\n\t\t\t\t\t\tme.paddingLeft = opts.position === 'bottom' ? (cosRotation * firstLabelWidth) + 3 : (cosRotation * lineSpace) + 3; // add 3 px to move away from canvas edges\n\t\t\t\t\t\tme.paddingRight = opts.position === 'bottom' ? (cosRotation * lineSpace) + 3 : (cosRotation * lastLabelWidth) + 3;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tme.paddingLeft = firstLabelWidth / 2 + 3; // add 3 px to move away from canvas edges\n\t\t\t\t\t\tme.paddingRight = lastLabelWidth / 2 + 3;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// A vertical axis is more constrained by the width. Labels are the\n\t\t\t\t\t// dominant factor here, so get that length first and account for padding\n\t\t\t\t\tif (tickOpts.mirror) {\n\t\t\t\t\t\tlargestTextWidth = 0;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// use lineSpace for consistency with horizontal axis\n\t\t\t\t\t\t// tickPadding is not implemented for horizontal\n\t\t\t\t\t\tlargestTextWidth += tickPadding + lineSpace;\n\t\t\t\t\t}\n\n\t\t\t\t\tminSize.width = Math.min(me.maxWidth, minSize.width + largestTextWidth);\n\n\t\t\t\t\tme.paddingTop = tickFont.size / 2;\n\t\t\t\t\tme.paddingBottom = tickFont.size / 2;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tme.handleMargins();\n\n\t\t\tme.width = minSize.width;\n\t\t\tme.height = minSize.height;\n\t\t},\n\n\t\t/**\n\t\t * Handle margins and padding interactions\n\t\t * @private\n\t\t */\n\t\thandleMargins: function() {\n\t\t\tvar me = this;\n\t\t\tif (me.margins) {\n\t\t\t\tme.paddingLeft = Math.max(me.paddingLeft - me.margins.left, 0);\n\t\t\t\tme.paddingTop = Math.max(me.paddingTop - me.margins.top, 0);\n\t\t\t\tme.paddingRight = Math.max(me.paddingRight - me.margins.right, 0);\n\t\t\t\tme.paddingBottom = Math.max(me.paddingBottom - me.margins.bottom, 0);\n\t\t\t}\n\t\t},\n\n\t\tafterFit: function() {\n\t\t\thelpers.callback(this.options.afterFit, [this]);\n\t\t},\n\n\t\t// Shared Methods\n\t\tisHorizontal: function() {\n\t\t\treturn this.options.position === 'top' || this.options.position === 'bottom';\n\t\t},\n\t\tisFullWidth: function() {\n\t\t\treturn (this.options.fullWidth);\n\t\t},\n\n\t\t// Get the correct value. NaN bad inputs, If the value type is object get the x or y based on whether we are horizontal or not\n\t\tgetRightValue: function(rawValue) {\n\t\t\t// Null and undefined values first\n\t\t\tif (helpers.isNullOrUndef(rawValue)) {\n\t\t\t\treturn NaN;\n\t\t\t}\n\t\t\t// isNaN(object) returns true, so make sure NaN is checking for a number; Discard Infinite values\n\t\t\tif (typeof rawValue === 'number' && !isFinite(rawValue)) {\n\t\t\t\treturn NaN;\n\t\t\t}\n\t\t\t// If it is in fact an object, dive in one more level\n\t\t\tif (rawValue) {\n\t\t\t\tif (this.isHorizontal()) {\n\t\t\t\t\tif (rawValue.x !== undefined) {\n\t\t\t\t\t\treturn this.getRightValue(rawValue.x);\n\t\t\t\t\t}\n\t\t\t\t} else if (rawValue.y !== undefined) {\n\t\t\t\t\treturn this.getRightValue(rawValue.y);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Value is good, return it\n\t\t\treturn rawValue;\n\t\t},\n\n\t\t/**\n\t\t * Used to get the value to display in the tooltip for the data at the given index\n\t\t * @param index\n\t\t * @param datasetIndex\n\t\t */\n\t\tgetLabelForIndex: helpers.noop,\n\n\t\t/**\n\t\t * Returns the location of the given data point. Value can either be an index or a numerical value\n\t\t * The coordinate (0, 0) is at the upper-left corner of the canvas\n\t\t * @param value\n\t\t * @param index\n\t\t * @param datasetIndex\n\t\t */\n\t\tgetPixelForValue: helpers.noop,\n\n\t\t/**\n\t\t * Used to get the data value from a given pixel. This is the inverse of getPixelForValue\n\t\t * The coordinate (0, 0) is at the upper-left corner of the canvas\n\t\t * @param pixel\n\t\t */\n\t\tgetValueForPixel: helpers.noop,\n\n\t\t/**\n\t\t * Returns the location of the tick at the given index\n\t\t * The coordinate (0, 0) is at the upper-left corner of the canvas\n\t\t */\n\t\tgetPixelForTick: function(index) {\n\t\t\tvar me = this;\n\t\t\tvar offset = me.options.offset;\n\t\t\tif (me.isHorizontal()) {\n\t\t\t\tvar innerWidth = me.width - (me.paddingLeft + me.paddingRight);\n\t\t\t\tvar tickWidth = innerWidth / Math.max((me._ticks.length - (offset ? 0 : 1)), 1);\n\t\t\t\tvar pixel = (tickWidth * index) + me.paddingLeft;\n\n\t\t\t\tif (offset) {\n\t\t\t\t\tpixel += tickWidth / 2;\n\t\t\t\t}\n\n\t\t\t\tvar finalVal = me.left + Math.round(pixel);\n\t\t\t\tfinalVal += me.isFullWidth() ? me.margins.left : 0;\n\t\t\t\treturn finalVal;\n\t\t\t}\n\t\t\tvar innerHeight = me.height - (me.paddingTop + me.paddingBottom);\n\t\t\treturn me.top + (index * (innerHeight / (me._ticks.length - 1)));\n\t\t},\n\n\t\t/**\n\t\t * Utility for getting the pixel location of a percentage of scale\n\t\t * The coordinate (0, 0) is at the upper-left corner of the canvas\n\t\t */\n\t\tgetPixelForDecimal: function(decimal) {\n\t\t\tvar me = this;\n\t\t\tif (me.isHorizontal()) {\n\t\t\t\tvar innerWidth = me.width - (me.paddingLeft + me.paddingRight);\n\t\t\t\tvar valueOffset = (innerWidth * decimal) + me.paddingLeft;\n\n\t\t\t\tvar finalVal = me.left + Math.round(valueOffset);\n\t\t\t\tfinalVal += me.isFullWidth() ? me.margins.left : 0;\n\t\t\t\treturn finalVal;\n\t\t\t}\n\t\t\treturn me.top + (decimal * me.height);\n\t\t},\n\n\t\t/**\n\t\t * Returns the pixel for the minimum chart value\n\t\t * The coordinate (0, 0) is at the upper-left corner of the canvas\n\t\t */\n\t\tgetBasePixel: function() {\n\t\t\treturn this.getPixelForValue(this.getBaseValue());\n\t\t},\n\n\t\tgetBaseValue: function() {\n\t\t\tvar me = this;\n\t\t\tvar min = me.min;\n\t\t\tvar max = me.max;\n\n\t\t\treturn me.beginAtZero ? 0 :\n\t\t\t\tmin < 0 && max < 0 ? max :\n\t\t\t\tmin > 0 && max > 0 ? min :\n\t\t\t\t0;\n\t\t},\n\n\t\t/**\n\t\t * Returns a subset of ticks to be plotted to avoid overlapping labels.\n\t\t * @private\n\t\t */\n\t\t_autoSkip: function(ticks) {\n\t\t\tvar skipRatio;\n\t\t\tvar me = this;\n\t\t\tvar isHorizontal = me.isHorizontal();\n\t\t\tvar optionTicks = me.options.ticks.minor;\n\t\t\tvar tickCount = ticks.length;\n\t\t\tvar labelRotationRadians = helpers.toRadians(me.labelRotation);\n\t\t\tvar cosRotation = Math.cos(labelRotationRadians);\n\t\t\tvar longestRotatedLabel = me.longestLabelWidth * cosRotation;\n\t\t\tvar result = [];\n\t\t\tvar i, tick, shouldSkip;\n\n\t\t\t// figure out the maximum number of gridlines to show\n\t\t\tvar maxTicks;\n\t\t\tif (optionTicks.maxTicksLimit) {\n\t\t\t\tmaxTicks = optionTicks.maxTicksLimit;\n\t\t\t}\n\n\t\t\tif (isHorizontal) {\n\t\t\t\tskipRatio = false;\n\n\t\t\t\tif ((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount > (me.width - (me.paddingLeft + me.paddingRight))) {\n\t\t\t\t\tskipRatio = 1 + Math.floor(((longestRotatedLabel + optionTicks.autoSkipPadding) * tickCount) / (me.width - (me.paddingLeft + me.paddingRight)));\n\t\t\t\t}\n\n\t\t\t\t// if they defined a max number of optionTicks,\n\t\t\t\t// increase skipRatio until that number is met\n\t\t\t\tif (maxTicks && tickCount > maxTicks) {\n\t\t\t\t\tskipRatio = Math.max(skipRatio, Math.floor(tickCount / maxTicks));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (i = 0; i < tickCount; i++) {\n\t\t\t\ttick = ticks[i];\n\n\t\t\t\t// Since we always show the last tick,we need may need to hide the last shown one before\n\t\t\t\tshouldSkip = (skipRatio > 1 && i % skipRatio > 0) || (i % skipRatio === 0 && i + skipRatio >= tickCount);\n\t\t\t\tif (shouldSkip && i !== tickCount - 1) {\n\t\t\t\t\t// leave tick in place but make sure it's not displayed (#4635)\n\t\t\t\t\tdelete tick.label;\n\t\t\t\t}\n\t\t\t\tresult.push(tick);\n\t\t\t}\n\t\t\treturn result;\n\t\t},\n\n\t\t// Actually draw the scale on the canvas\n\t\t// @param {rectangle} chartArea : the area of the chart to draw full grid lines on\n\t\tdraw: function(chartArea) {\n\t\t\tvar me = this;\n\t\t\tvar options = me.options;\n\t\t\tif (!options.display) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar context = me.ctx;\n\t\t\tvar globalDefaults = defaults.global;\n\t\t\tvar optionTicks = options.ticks.minor;\n\t\t\tvar optionMajorTicks = options.ticks.major || optionTicks;\n\t\t\tvar gridLines = options.gridLines;\n\t\t\tvar scaleLabel = options.scaleLabel;\n\n\t\t\tvar isRotated = me.labelRotation !== 0;\n\t\t\tvar isHorizontal = me.isHorizontal();\n\n\t\t\tvar ticks = optionTicks.autoSkip ? me._autoSkip(me.getTicks()) : me.getTicks();\n\t\t\tvar tickFontColor = helpers.valueOrDefault(optionTicks.fontColor, globalDefaults.defaultFontColor);\n\t\t\tvar tickFont = parseFontOptions(optionTicks);\n\t\t\tvar majorTickFontColor = helpers.valueOrDefault(optionMajorTicks.fontColor, globalDefaults.defaultFontColor);\n\t\t\tvar majorTickFont = parseFontOptions(optionMajorTicks);\n\n\t\t\tvar tl = gridLines.drawTicks ? gridLines.tickMarkLength : 0;\n\n\t\t\tvar scaleLabelFontColor = helpers.valueOrDefault(scaleLabel.fontColor, globalDefaults.defaultFontColor);\n\t\t\tvar scaleLabelFont = parseFontOptions(scaleLabel);\n\t\t\tvar scaleLabelPadding = helpers.options.toPadding(scaleLabel.padding);\n\t\t\tvar labelRotationRadians = helpers.toRadians(me.labelRotation);\n\n\t\t\tvar itemsToDraw = [];\n\n\t\t\tvar axisWidth = me.options.gridLines.lineWidth;\n\t\t\tvar xTickStart = options.position === 'right' ? me.right : me.right - axisWidth - tl;\n\t\t\tvar xTickEnd = options.position === 'right' ? me.right + tl : me.right;\n\t\t\tvar yTickStart = options.position === 'bottom' ? me.top + axisWidth : me.bottom - tl - axisWidth;\n\t\t\tvar yTickEnd = options.position === 'bottom' ? me.top + axisWidth + tl : me.bottom + axisWidth;\n\n\t\t\thelpers.each(ticks, function(tick, index) {\n\t\t\t\t// autoskipper skipped this tick (#4635)\n\t\t\t\tif (helpers.isNullOrUndef(tick.label)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tvar label = tick.label;\n\t\t\t\tvar lineWidth, lineColor, borderDash, borderDashOffset;\n\t\t\t\tif (index === me.zeroLineIndex && options.offset === gridLines.offsetGridLines) {\n\t\t\t\t\t// Draw the first index specially\n\t\t\t\t\tlineWidth = gridLines.zeroLineWidth;\n\t\t\t\t\tlineColor = gridLines.zeroLineColor;\n\t\t\t\t\tborderDash = gridLines.zeroLineBorderDash;\n\t\t\t\t\tborderDashOffset = gridLines.zeroLineBorderDashOffset;\n\t\t\t\t} else {\n\t\t\t\t\tlineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, index);\n\t\t\t\t\tlineColor = helpers.valueAtIndexOrDefault(gridLines.color, index);\n\t\t\t\t\tborderDash = helpers.valueOrDefault(gridLines.borderDash, globalDefaults.borderDash);\n\t\t\t\t\tborderDashOffset = helpers.valueOrDefault(gridLines.borderDashOffset, globalDefaults.borderDashOffset);\n\t\t\t\t}\n\n\t\t\t\t// Common properties\n\t\t\t\tvar tx1, ty1, tx2, ty2, x1, y1, x2, y2, labelX, labelY;\n\t\t\t\tvar textAlign = 'middle';\n\t\t\t\tvar textBaseline = 'middle';\n\t\t\t\tvar tickPadding = optionTicks.padding;\n\n\t\t\t\tif (isHorizontal) {\n\t\t\t\t\tvar labelYOffset = tl + tickPadding;\n\n\t\t\t\t\tif (options.position === 'bottom') {\n\t\t\t\t\t\t// bottom\n\t\t\t\t\t\ttextBaseline = !isRotated ? 'top' : 'middle';\n\t\t\t\t\t\ttextAlign = !isRotated ? 'center' : 'right';\n\t\t\t\t\t\tlabelY = me.top + labelYOffset;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// top\n\t\t\t\t\t\ttextBaseline = !isRotated ? 'bottom' : 'middle';\n\t\t\t\t\t\ttextAlign = !isRotated ? 'center' : 'left';\n\t\t\t\t\t\tlabelY = me.bottom - labelYOffset;\n\t\t\t\t\t}\n\n\t\t\t\t\tvar xLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1);\n\t\t\t\t\tif (xLineValue < me.left) {\n\t\t\t\t\t\tlineColor = 'rgba(0,0,0,0)';\n\t\t\t\t\t}\n\t\t\t\t\txLineValue += helpers.aliasPixel(lineWidth);\n\n\t\t\t\t\tlabelX = me.getPixelForTick(index) + optionTicks.labelOffset; // x values for optionTicks (need to consider offsetLabel option)\n\n\t\t\t\t\ttx1 = tx2 = x1 = x2 = xLineValue;\n\t\t\t\t\tty1 = yTickStart;\n\t\t\t\t\tty2 = yTickEnd;\n\t\t\t\t\ty1 = chartArea.top;\n\t\t\t\t\ty2 = chartArea.bottom + axisWidth;\n\t\t\t\t} else {\n\t\t\t\t\tvar isLeft = options.position === 'left';\n\t\t\t\t\tvar labelXOffset;\n\n\t\t\t\t\tif (optionTicks.mirror) {\n\t\t\t\t\t\ttextAlign = isLeft ? 'left' : 'right';\n\t\t\t\t\t\tlabelXOffset = tickPadding;\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttextAlign = isLeft ? 'right' : 'left';\n\t\t\t\t\t\tlabelXOffset = tl + tickPadding;\n\t\t\t\t\t}\n\n\t\t\t\t\tlabelX = isLeft ? me.right - labelXOffset : me.left + labelXOffset;\n\n\t\t\t\t\tvar yLineValue = getLineValue(me, index, gridLines.offsetGridLines && ticks.length > 1);\n\t\t\t\t\tif (yLineValue < me.top) {\n\t\t\t\t\t\tlineColor = 'rgba(0,0,0,0)';\n\t\t\t\t\t}\n\t\t\t\t\tyLineValue += helpers.aliasPixel(lineWidth);\n\n\t\t\t\t\tlabelY = me.getPixelForTick(index) + optionTicks.labelOffset;\n\n\t\t\t\t\ttx1 = xTickStart;\n\t\t\t\t\ttx2 = xTickEnd;\n\t\t\t\t\tx1 = chartArea.left;\n\t\t\t\t\tx2 = chartArea.right + axisWidth;\n\t\t\t\t\tty1 = ty2 = y1 = y2 = yLineValue;\n\t\t\t\t}\n\n\t\t\t\titemsToDraw.push({\n\t\t\t\t\ttx1: tx1,\n\t\t\t\t\tty1: ty1,\n\t\t\t\t\ttx2: tx2,\n\t\t\t\t\tty2: ty2,\n\t\t\t\t\tx1: x1,\n\t\t\t\t\ty1: y1,\n\t\t\t\t\tx2: x2,\n\t\t\t\t\ty2: y2,\n\t\t\t\t\tlabelX: labelX,\n\t\t\t\t\tlabelY: labelY,\n\t\t\t\t\tglWidth: lineWidth,\n\t\t\t\t\tglColor: lineColor,\n\t\t\t\t\tglBorderDash: borderDash,\n\t\t\t\t\tglBorderDashOffset: borderDashOffset,\n\t\t\t\t\trotation: -1 * labelRotationRadians,\n\t\t\t\t\tlabel: label,\n\t\t\t\t\tmajor: tick.major,\n\t\t\t\t\ttextBaseline: textBaseline,\n\t\t\t\t\ttextAlign: textAlign\n\t\t\t\t});\n\t\t\t});\n\n\t\t\t// Draw all of the tick labels, tick marks, and grid lines at the correct places\n\t\t\thelpers.each(itemsToDraw, function(itemToDraw) {\n\t\t\t\tif (gridLines.display) {\n\t\t\t\t\tcontext.save();\n\t\t\t\t\tcontext.lineWidth = itemToDraw.glWidth;\n\t\t\t\t\tcontext.strokeStyle = itemToDraw.glColor;\n\t\t\t\t\tif (context.setLineDash) {\n\t\t\t\t\t\tcontext.setLineDash(itemToDraw.glBorderDash);\n\t\t\t\t\t\tcontext.lineDashOffset = itemToDraw.glBorderDashOffset;\n\t\t\t\t\t}\n\n\t\t\t\t\tcontext.beginPath();\n\n\t\t\t\t\tif (gridLines.drawTicks) {\n\t\t\t\t\t\tcontext.moveTo(itemToDraw.tx1, itemToDraw.ty1);\n\t\t\t\t\t\tcontext.lineTo(itemToDraw.tx2, itemToDraw.ty2);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (gridLines.drawOnChartArea) {\n\t\t\t\t\t\tcontext.moveTo(itemToDraw.x1, itemToDraw.y1);\n\t\t\t\t\t\tcontext.lineTo(itemToDraw.x2, itemToDraw.y2);\n\t\t\t\t\t}\n\n\t\t\t\t\tcontext.stroke();\n\t\t\t\t\tcontext.restore();\n\t\t\t\t}\n\n\t\t\t\tif (optionTicks.display) {\n\t\t\t\t\t// Make sure we draw text in the correct color and font\n\t\t\t\t\tcontext.save();\n\t\t\t\t\tcontext.translate(itemToDraw.labelX, itemToDraw.labelY);\n\t\t\t\t\tcontext.rotate(itemToDraw.rotation);\n\t\t\t\t\tcontext.font = itemToDraw.major ? majorTickFont.font : tickFont.font;\n\t\t\t\t\tcontext.fillStyle = itemToDraw.major ? majorTickFontColor : tickFontColor;\n\t\t\t\t\tcontext.textBaseline = itemToDraw.textBaseline;\n\t\t\t\t\tcontext.textAlign = itemToDraw.textAlign;\n\n\t\t\t\t\tvar label = itemToDraw.label;\n\t\t\t\t\tif (helpers.isArray(label)) {\n\t\t\t\t\t\tvar lineCount = label.length;\n\t\t\t\t\t\tvar lineHeight = tickFont.size * 1.5;\n\t\t\t\t\t\tvar y = me.isHorizontal() ? 0 : -lineHeight * (lineCount - 1) / 2;\n\n\t\t\t\t\t\tfor (var i = 0; i < lineCount; ++i) {\n\t\t\t\t\t\t\t// We just make sure the multiline element is a string here..\n\t\t\t\t\t\t\tcontext.fillText('' + label[i], 0, y);\n\t\t\t\t\t\t\t// apply same lineSpacing as calculated @ L#320\n\t\t\t\t\t\t\ty += lineHeight;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcontext.fillText(label, 0, 0);\n\t\t\t\t\t}\n\t\t\t\t\tcontext.restore();\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tif (scaleLabel.display) {\n\t\t\t\t// Draw the scale label\n\t\t\t\tvar scaleLabelX;\n\t\t\t\tvar scaleLabelY;\n\t\t\t\tvar rotation = 0;\n\t\t\t\tvar halfLineHeight = parseLineHeight(scaleLabel) / 2;\n\n\t\t\t\tif (isHorizontal) {\n\t\t\t\t\tscaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width\n\t\t\t\t\tscaleLabelY = options.position === 'bottom'\n\t\t\t\t\t\t? me.bottom - halfLineHeight - scaleLabelPadding.bottom\n\t\t\t\t\t\t: me.top + halfLineHeight + scaleLabelPadding.top;\n\t\t\t\t} else {\n\t\t\t\t\tvar isLeft = options.position === 'left';\n\t\t\t\t\tscaleLabelX = isLeft\n\t\t\t\t\t\t? me.left + halfLineHeight + scaleLabelPadding.top\n\t\t\t\t\t\t: me.right - halfLineHeight - scaleLabelPadding.top;\n\t\t\t\t\tscaleLabelY = me.top + ((me.bottom - me.top) / 2);\n\t\t\t\t\trotation = isLeft ? -0.5 * Math.PI : 0.5 * Math.PI;\n\t\t\t\t}\n\n\t\t\t\tcontext.save();\n\t\t\t\tcontext.translate(scaleLabelX, scaleLabelY);\n\t\t\t\tcontext.rotate(rotation);\n\t\t\t\tcontext.textAlign = 'center';\n\t\t\t\tcontext.textBaseline = 'middle';\n\t\t\t\tcontext.fillStyle = scaleLabelFontColor; // render in correct colour\n\t\t\t\tcontext.font = scaleLabelFont.font;\n\t\t\t\tcontext.fillText(scaleLabel.labelString, 0, 0);\n\t\t\t\tcontext.restore();\n\t\t\t}\n\n\t\t\tif (gridLines.drawBorder) {\n\t\t\t\t// Draw the line at the edge of the axis\n\t\t\t\tcontext.lineWidth = helpers.valueAtIndexOrDefault(gridLines.lineWidth, 0);\n\t\t\t\tcontext.strokeStyle = helpers.valueAtIndexOrDefault(gridLines.color, 0);\n\t\t\t\tvar x1 = me.left;\n\t\t\t\tvar x2 = me.right + axisWidth;\n\t\t\t\tvar y1 = me.top;\n\t\t\t\tvar y2 = me.bottom + axisWidth;\n\n\t\t\t\tvar aliasPixel = helpers.aliasPixel(context.lineWidth);\n\t\t\t\tif (isHorizontal) {\n\t\t\t\t\ty1 = y2 = options.position === 'top' ? me.bottom : me.top;\n\t\t\t\t\ty1 += aliasPixel;\n\t\t\t\t\ty2 += aliasPixel;\n\t\t\t\t} else {\n\t\t\t\t\tx1 = x2 = options.position === 'left' ? me.right : me.left;\n\t\t\t\t\tx1 += aliasPixel;\n\t\t\t\t\tx2 += aliasPixel;\n\t\t\t\t}\n\n\t\t\t\tcontext.beginPath();\n\t\t\t\tcontext.moveTo(x1, y1);\n\t\t\t\tcontext.lineTo(x2, y2);\n\t\t\t\tcontext.stroke();\n\t\t\t}\n\t\t}\n\t});\n};\n\n},{\"25\":25,\"26\":26,\"34\":34,\"45\":45}],33:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\nvar helpers = require(45);\nvar layouts = require(30);\n\nmodule.exports = function(Chart) {\n\n\tChart.scaleService = {\n\t\t// Scale registration object. Extensions can register new scale types (such as log or DB scales) and then\n\t\t// use the new chart options to grab the correct scale\n\t\tconstructors: {},\n\t\t// Use a registration function so that we can move to an ES6 map when we no longer need to support\n\t\t// old browsers\n\n\t\t// Scale config defaults\n\t\tdefaults: {},\n\t\tregisterScaleType: function(type, scaleConstructor, scaleDefaults) {\n\t\t\tthis.constructors[type] = scaleConstructor;\n\t\t\tthis.defaults[type] = helpers.clone(scaleDefaults);\n\t\t},\n\t\tgetScaleConstructor: function(type) {\n\t\t\treturn this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;\n\t\t},\n\t\tgetScaleDefaults: function(type) {\n\t\t\t// Return the scale defaults merged with the global settings so that we always use the latest ones\n\t\t\treturn this.defaults.hasOwnProperty(type) ? helpers.merge({}, [defaults.scale, this.defaults[type]]) : {};\n\t\t},\n\t\tupdateScaleDefaults: function(type, additions) {\n\t\t\tvar me = this;\n\t\t\tif (me.defaults.hasOwnProperty(type)) {\n\t\t\t\tme.defaults[type] = helpers.extend(me.defaults[type], additions);\n\t\t\t}\n\t\t},\n\t\taddScalesToLayout: function(chart) {\n\t\t\t// Adds each scale to the chart.boxes array to be sized accordingly\n\t\t\thelpers.each(chart.scales, function(scale) {\n\t\t\t\t// Set ILayoutItem parameters for backwards compatibility\n\t\t\t\tscale.fullWidth = scale.options.fullWidth;\n\t\t\t\tscale.position = scale.options.position;\n\t\t\t\tscale.weight = scale.options.weight;\n\t\t\t\tlayouts.addBox(chart, scale);\n\t\t\t});\n\t\t}\n\t};\n};\n\n},{\"25\":25,\"30\":30,\"45\":45}],34:[function(require,module,exports){\n'use strict';\n\nvar helpers = require(45);\n\n/**\n * Namespace to hold static tick generation functions\n * @namespace Chart.Ticks\n */\nmodule.exports = {\n\t/**\n\t * Namespace to hold formatters for different types of ticks\n\t * @namespace Chart.Ticks.formatters\n\t */\n\tformatters: {\n\t\t/**\n\t\t * Formatter for value labels\n\t\t * @method Chart.Ticks.formatters.values\n\t\t * @param value the value to display\n\t\t * @return {String|Array} the label to display\n\t\t */\n\t\tvalues: function(value) {\n\t\t\treturn helpers.isArray(value) ? value : '' + value;\n\t\t},\n\n\t\t/**\n\t\t * Formatter for linear numeric ticks\n\t\t * @method Chart.Ticks.formatters.linear\n\t\t * @param tickValue {Number} the value to be formatted\n\t\t * @param index {Number} the position of the tickValue parameter in the ticks array\n\t\t * @param ticks {Array<Number>} the list of ticks being converted\n\t\t * @return {String} string representation of the tickValue parameter\n\t\t */\n\t\tlinear: function(tickValue, index, ticks) {\n\t\t\t// If we have lots of ticks, don't use the ones\n\t\t\tvar delta = ticks.length > 3 ? ticks[2] - ticks[1] : ticks[1] - ticks[0];\n\n\t\t\t// If we have a number like 2.5 as the delta, figure out how many decimal places we need\n\t\t\tif (Math.abs(delta) > 1) {\n\t\t\t\tif (tickValue !== Math.floor(tickValue)) {\n\t\t\t\t\t// not an integer\n\t\t\t\t\tdelta = tickValue - Math.floor(tickValue);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar logDelta = helpers.log10(Math.abs(delta));\n\t\t\tvar tickString = '';\n\n\t\t\tif (tickValue !== 0) {\n\t\t\t\tvar numDecimal = -1 * Math.floor(logDelta);\n\t\t\t\tnumDecimal = Math.max(Math.min(numDecimal, 20), 0); // toFixed has a max of 20 decimal places\n\t\t\t\ttickString = tickValue.toFixed(numDecimal);\n\t\t\t} else {\n\t\t\t\ttickString = '0'; // never show decimal places for 0\n\t\t\t}\n\n\t\t\treturn tickString;\n\t\t},\n\n\t\tlogarithmic: function(tickValue, index, ticks) {\n\t\t\tvar remain = tickValue / (Math.pow(10, Math.floor(helpers.log10(tickValue))));\n\n\t\t\tif (tickValue === 0) {\n\t\t\t\treturn '0';\n\t\t\t} else if (remain === 1 || remain === 2 || remain === 5 || index === 0 || index === ticks.length - 1) {\n\t\t\t\treturn tickValue.toExponential();\n\t\t\t}\n\t\t\treturn '';\n\t\t}\n\t}\n};\n\n},{\"45\":45}],35:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\nvar Element = require(26);\nvar helpers = require(45);\n\ndefaults._set('global', {\n\ttooltips: {\n\t\tenabled: true,\n\t\tcustom: null,\n\t\tmode: 'nearest',\n\t\tposition: 'average',\n\t\tintersect: true,\n\t\tbackgroundColor: 'rgba(0,0,0,0.8)',\n\t\ttitleFontStyle: 'bold',\n\t\ttitleSpacing: 2,\n\t\ttitleMarginBottom: 6,\n\t\ttitleFontColor: '#fff',\n\t\ttitleAlign: 'left',\n\t\tbodySpacing: 2,\n\t\tbodyFontColor: '#fff',\n\t\tbodyAlign: 'left',\n\t\tfooterFontStyle: 'bold',\n\t\tfooterSpacing: 2,\n\t\tfooterMarginTop: 6,\n\t\tfooterFontColor: '#fff',\n\t\tfooterAlign: 'left',\n\t\tyPadding: 6,\n\t\txPadding: 6,\n\t\tcaretPadding: 2,\n\t\tcaretSize: 5,\n\t\tcornerRadius: 6,\n\t\tmultiKeyBackground: '#fff',\n\t\tdisplayColors: true,\n\t\tborderColor: 'rgba(0,0,0,0)',\n\t\tborderWidth: 0,\n\t\tcallbacks: {\n\t\t\t// Args are: (tooltipItems, data)\n\t\t\tbeforeTitle: helpers.noop,\n\t\t\ttitle: function(tooltipItems, data) {\n\t\t\t\t// Pick first xLabel for now\n\t\t\t\tvar title = '';\n\t\t\t\tvar labels = data.labels;\n\t\t\t\tvar labelCount = labels ? labels.length : 0;\n\n\t\t\t\tif (tooltipItems.length > 0) {\n\t\t\t\t\tvar item = tooltipItems[0];\n\n\t\t\t\t\tif (item.xLabel) {\n\t\t\t\t\t\ttitle = item.xLabel;\n\t\t\t\t\t} else if (labelCount > 0 && item.index < labelCount) {\n\t\t\t\t\t\ttitle = labels[item.index];\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn title;\n\t\t\t},\n\t\t\tafterTitle: helpers.noop,\n\n\t\t\t// Args are: (tooltipItems, data)\n\t\t\tbeforeBody: helpers.noop,\n\n\t\t\t// Args are: (tooltipItem, data)\n\t\t\tbeforeLabel: helpers.noop,\n\t\t\tlabel: function(tooltipItem, data) {\n\t\t\t\tvar label = data.datasets[tooltipItem.datasetIndex].label || '';\n\n\t\t\t\tif (label) {\n\t\t\t\t\tlabel += ': ';\n\t\t\t\t}\n\t\t\t\tlabel += tooltipItem.yLabel;\n\t\t\t\treturn label;\n\t\t\t},\n\t\t\tlabelColor: function(tooltipItem, chart) {\n\t\t\t\tvar meta = chart.getDatasetMeta(tooltipItem.datasetIndex);\n\t\t\t\tvar activeElement = meta.data[tooltipItem.index];\n\t\t\t\tvar view = activeElement._view;\n\t\t\t\treturn {\n\t\t\t\t\tborderColor: view.borderColor,\n\t\t\t\t\tbackgroundColor: view.backgroundColor\n\t\t\t\t};\n\t\t\t},\n\t\t\tlabelTextColor: function() {\n\t\t\t\treturn this._options.bodyFontColor;\n\t\t\t},\n\t\t\tafterLabel: helpers.noop,\n\n\t\t\t// Args are: (tooltipItems, data)\n\t\t\tafterBody: helpers.noop,\n\n\t\t\t// Args are: (tooltipItems, data)\n\t\t\tbeforeFooter: helpers.noop,\n\t\t\tfooter: helpers.noop,\n\t\t\tafterFooter: helpers.noop\n\t\t}\n\t}\n});\n\nmodule.exports = function(Chart) {\n\n\t/**\n \t * Helper method to merge the opacity into a color\n \t */\n\tfunction mergeOpacity(colorString, opacity) {\n\t\tvar color = helpers.color(colorString);\n\t\treturn color.alpha(opacity * color.alpha()).rgbaString();\n\t}\n\n\t// Helper to push or concat based on if the 2nd parameter is an array or not\n\tfunction pushOrConcat(base, toPush) {\n\t\tif (toPush) {\n\t\t\tif (helpers.isArray(toPush)) {\n\t\t\t\t// base = base.concat(toPush);\n\t\t\t\tArray.prototype.push.apply(base, toPush);\n\t\t\t} else {\n\t\t\t\tbase.push(toPush);\n\t\t\t}\n\t\t}\n\n\t\treturn base;\n\t}\n\n\t// Private helper to create a tooltip item model\n\t// @param element : the chart element (point, arc, bar) to create the tooltip item for\n\t// @return : new tooltip item\n\tfunction createTooltipItem(element) {\n\t\tvar xScale = element._xScale;\n\t\tvar yScale = element._yScale || element._scale; // handle radar || polarArea charts\n\t\tvar index = element._index;\n\t\tvar datasetIndex = element._datasetIndex;\n\n\t\treturn {\n\t\t\txLabel: xScale ? xScale.getLabelForIndex(index, datasetIndex) : '',\n\t\t\tyLabel: yScale ? yScale.getLabelForIndex(index, datasetIndex) : '',\n\t\t\tindex: index,\n\t\t\tdatasetIndex: datasetIndex,\n\t\t\tx: element._model.x,\n\t\t\ty: element._model.y\n\t\t};\n\t}\n\n\t/**\n\t * Helper to get the reset model for the tooltip\n\t * @param tooltipOpts {Object} the tooltip options\n\t */\n\tfunction getBaseModel(tooltipOpts) {\n\t\tvar globalDefaults = defaults.global;\n\t\tvar valueOrDefault = helpers.valueOrDefault;\n\n\t\treturn {\n\t\t\t// Positioning\n\t\t\txPadding: tooltipOpts.xPadding,\n\t\t\tyPadding: tooltipOpts.yPadding,\n\t\t\txAlign: tooltipOpts.xAlign,\n\t\t\tyAlign: tooltipOpts.yAlign,\n\n\t\t\t// Body\n\t\t\tbodyFontColor: tooltipOpts.bodyFontColor,\n\t\t\t_bodyFontFamily: valueOrDefault(tooltipOpts.bodyFontFamily, globalDefaults.defaultFontFamily),\n\t\t\t_bodyFontStyle: valueOrDefault(tooltipOpts.bodyFontStyle, globalDefaults.defaultFontStyle),\n\t\t\t_bodyAlign: tooltipOpts.bodyAlign,\n\t\t\tbodyFontSize: valueOrDefault(tooltipOpts.bodyFontSize, globalDefaults.defaultFontSize),\n\t\t\tbodySpacing: tooltipOpts.bodySpacing,\n\n\t\t\t// Title\n\t\t\ttitleFontColor: tooltipOpts.titleFontColor,\n\t\t\t_titleFontFamily: valueOrDefault(tooltipOpts.titleFontFamily, globalDefaults.defaultFontFamily),\n\t\t\t_titleFontStyle: valueOrDefault(tooltipOpts.titleFontStyle, globalDefaults.defaultFontStyle),\n\t\t\ttitleFontSize: valueOrDefault(tooltipOpts.titleFontSize, globalDefaults.defaultFontSize),\n\t\t\t_titleAlign: tooltipOpts.titleAlign,\n\t\t\ttitleSpacing: tooltipOpts.titleSpacing,\n\t\t\ttitleMarginBottom: tooltipOpts.titleMarginBottom,\n\n\t\t\t// Footer\n\t\t\tfooterFontColor: tooltipOpts.footerFontColor,\n\t\t\t_footerFontFamily: valueOrDefault(tooltipOpts.footerFontFamily, globalDefaults.defaultFontFamily),\n\t\t\t_footerFontStyle: valueOrDefault(tooltipOpts.footerFontStyle, globalDefaults.defaultFontStyle),\n\t\t\tfooterFontSize: valueOrDefault(tooltipOpts.footerFontSize, globalDefaults.defaultFontSize),\n\t\t\t_footerAlign: tooltipOpts.footerAlign,\n\t\t\tfooterSpacing: tooltipOpts.footerSpacing,\n\t\t\tfooterMarginTop: tooltipOpts.footerMarginTop,\n\n\t\t\t// Appearance\n\t\t\tcaretSize: tooltipOpts.caretSize,\n\t\t\tcornerRadius: tooltipOpts.cornerRadius,\n\t\t\tbackgroundColor: tooltipOpts.backgroundColor,\n\t\t\topacity: 0,\n\t\t\tlegendColorBackground: tooltipOpts.multiKeyBackground,\n\t\t\tdisplayColors: tooltipOpts.displayColors,\n\t\t\tborderColor: tooltipOpts.borderColor,\n\t\t\tborderWidth: tooltipOpts.borderWidth\n\t\t};\n\t}\n\n\t/**\n\t * Get the size of the tooltip\n\t */\n\tfunction getTooltipSize(tooltip, model) {\n\t\tvar ctx = tooltip._chart.ctx;\n\n\t\tvar height = model.yPadding * 2; // Tooltip Padding\n\t\tvar width = 0;\n\n\t\t// Count of all lines in the body\n\t\tvar body = model.body;\n\t\tvar combinedBodyLength = body.reduce(function(count, bodyItem) {\n\t\t\treturn count + bodyItem.before.length + bodyItem.lines.length + bodyItem.after.length;\n\t\t}, 0);\n\t\tcombinedBodyLength += model.beforeBody.length + model.afterBody.length;\n\n\t\tvar titleLineCount = model.title.length;\n\t\tvar footerLineCount = model.footer.length;\n\t\tvar titleFontSize = model.titleFontSize;\n\t\tvar bodyFontSize = model.bodyFontSize;\n\t\tvar footerFontSize = model.footerFontSize;\n\n\t\theight += titleLineCount * titleFontSize; // Title Lines\n\t\theight += titleLineCount ? (titleLineCount - 1) * model.titleSpacing : 0; // Title Line Spacing\n\t\theight += titleLineCount ? model.titleMarginBottom : 0; // Title's bottom Margin\n\t\theight += combinedBodyLength * bodyFontSize; // Body Lines\n\t\theight += combinedBodyLength ? (combinedBodyLength - 1) * model.bodySpacing : 0; // Body Line Spacing\n\t\theight += footerLineCount ? model.footerMarginTop : 0; // Footer Margin\n\t\theight += footerLineCount * (footerFontSize); // Footer Lines\n\t\theight += footerLineCount ? (footerLineCount - 1) * model.footerSpacing : 0; // Footer Line Spacing\n\n\t\t// Title width\n\t\tvar widthPadding = 0;\n\t\tvar maxLineWidth = function(line) {\n\t\t\twidth = Math.max(width, ctx.measureText(line).width + widthPadding);\n\t\t};\n\n\t\tctx.font = helpers.fontString(titleFontSize, model._titleFontStyle, model._titleFontFamily);\n\t\thelpers.each(model.title, maxLineWidth);\n\n\t\t// Body width\n\t\tctx.font = helpers.fontString(bodyFontSize, model._bodyFontStyle, model._bodyFontFamily);\n\t\thelpers.each(model.beforeBody.concat(model.afterBody), maxLineWidth);\n\n\t\t// Body lines may include some extra width due to the color box\n\t\twidthPadding = model.displayColors ? (bodyFontSize + 2) : 0;\n\t\thelpers.each(body, function(bodyItem) {\n\t\t\thelpers.each(bodyItem.before, maxLineWidth);\n\t\t\thelpers.each(bodyItem.lines, maxLineWidth);\n\t\t\thelpers.each(bodyItem.after, maxLineWidth);\n\t\t});\n\n\t\t// Reset back to 0\n\t\twidthPadding = 0;\n\n\t\t// Footer width\n\t\tctx.font = helpers.fontString(footerFontSize, model._footerFontStyle, model._footerFontFamily);\n\t\thelpers.each(model.footer, maxLineWidth);\n\n\t\t// Add padding\n\t\twidth += 2 * model.xPadding;\n\n\t\treturn {\n\t\t\twidth: width,\n\t\t\theight: height\n\t\t};\n\t}\n\n\t/**\n\t * Helper to get the alignment of a tooltip given the size\n\t */\n\tfunction determineAlignment(tooltip, size) {\n\t\tvar model = tooltip._model;\n\t\tvar chart = tooltip._chart;\n\t\tvar chartArea = tooltip._chart.chartArea;\n\t\tvar xAlign = 'center';\n\t\tvar yAlign = 'center';\n\n\t\tif (model.y < size.height) {\n\t\t\tyAlign = 'top';\n\t\t} else if (model.y > (chart.height - size.height)) {\n\t\t\tyAlign = 'bottom';\n\t\t}\n\n\t\tvar lf, rf; // functions to determine left, right alignment\n\t\tvar olf, orf; // functions to determine if left/right alignment causes tooltip to go outside chart\n\t\tvar yf; // function to get the y alignment if the tooltip goes outside of the left or right edges\n\t\tvar midX = (chartArea.left + chartArea.right) / 2;\n\t\tvar midY = (chartArea.top + chartArea.bottom) / 2;\n\n\t\tif (yAlign === 'center') {\n\t\t\tlf = function(x) {\n\t\t\t\treturn x <= midX;\n\t\t\t};\n\t\t\trf = function(x) {\n\t\t\t\treturn x > midX;\n\t\t\t};\n\t\t} else {\n\t\t\tlf = function(x) {\n\t\t\t\treturn x <= (size.width / 2);\n\t\t\t};\n\t\t\trf = function(x) {\n\t\t\t\treturn x >= (chart.width - (size.width / 2));\n\t\t\t};\n\t\t}\n\n\t\tolf = function(x) {\n\t\t\treturn x + size.width + model.caretSize + model.caretPadding > chart.width;\n\t\t};\n\t\torf = function(x) {\n\t\t\treturn x - size.width - model.caretSize - model.caretPadding < 0;\n\t\t};\n\t\tyf = function(y) {\n\t\t\treturn y <= midY ? 'top' : 'bottom';\n\t\t};\n\n\t\tif (lf(model.x)) {\n\t\t\txAlign = 'left';\n\n\t\t\t// Is tooltip too wide and goes over the right side of the chart.?\n\t\t\tif (olf(model.x)) {\n\t\t\t\txAlign = 'center';\n\t\t\t\tyAlign = yf(model.y);\n\t\t\t}\n\t\t} else if (rf(model.x)) {\n\t\t\txAlign = 'right';\n\n\t\t\t// Is tooltip too wide and goes outside left edge of canvas?\n\t\t\tif (orf(model.x)) {\n\t\t\t\txAlign = 'center';\n\t\t\t\tyAlign = yf(model.y);\n\t\t\t}\n\t\t}\n\n\t\tvar opts = tooltip._options;\n\t\treturn {\n\t\t\txAlign: opts.xAlign ? opts.xAlign : xAlign,\n\t\t\tyAlign: opts.yAlign ? opts.yAlign : yAlign\n\t\t};\n\t}\n\n\t/**\n\t * @Helper to get the location a tooltip needs to be placed at given the initial position (via the vm) and the size and alignment\n\t */\n\tfunction getBackgroundPoint(vm, size, alignment, chart) {\n\t\t// Background Position\n\t\tvar x = vm.x;\n\t\tvar y = vm.y;\n\n\t\tvar caretSize = vm.caretSize;\n\t\tvar caretPadding = vm.caretPadding;\n\t\tvar cornerRadius = vm.cornerRadius;\n\t\tvar xAlign = alignment.xAlign;\n\t\tvar yAlign = alignment.yAlign;\n\t\tvar paddingAndSize = caretSize + caretPadding;\n\t\tvar radiusAndPadding = cornerRadius + caretPadding;\n\n\t\tif (xAlign === 'right') {\n\t\t\tx -= size.width;\n\t\t} else if (xAlign === 'center') {\n\t\t\tx -= (size.width / 2);\n\t\t\tif (x + size.width > chart.width) {\n\t\t\t\tx = chart.width - size.width;\n\t\t\t}\n\t\t\tif (x < 0) {\n\t\t\t\tx = 0;\n\t\t\t}\n\t\t}\n\n\t\tif (yAlign === 'top') {\n\t\t\ty += paddingAndSize;\n\t\t} else if (yAlign === 'bottom') {\n\t\t\ty -= size.height + paddingAndSize;\n\t\t} else {\n\t\t\ty -= (size.height / 2);\n\t\t}\n\n\t\tif (yAlign === 'center') {\n\t\t\tif (xAlign === 'left') {\n\t\t\t\tx += paddingAndSize;\n\t\t\t} else if (xAlign === 'right') {\n\t\t\t\tx -= paddingAndSize;\n\t\t\t}\n\t\t} else if (xAlign === 'left') {\n\t\t\tx -= radiusAndPadding;\n\t\t} else if (xAlign === 'right') {\n\t\t\tx += radiusAndPadding;\n\t\t}\n\n\t\treturn {\n\t\t\tx: x,\n\t\t\ty: y\n\t\t};\n\t}\n\n\tChart.Tooltip = Element.extend({\n\t\tinitialize: function() {\n\t\t\tthis._model = getBaseModel(this._options);\n\t\t\tthis._lastActive = [];\n\t\t},\n\n\t\t// Get the title\n\t\t// Args are: (tooltipItem, data)\n\t\tgetTitle: function() {\n\t\t\tvar me = this;\n\t\t\tvar opts = me._options;\n\t\t\tvar callbacks = opts.callbacks;\n\n\t\t\tvar beforeTitle = callbacks.beforeTitle.apply(me, arguments);\n\t\t\tvar title = callbacks.title.apply(me, arguments);\n\t\t\tvar afterTitle = callbacks.afterTitle.apply(me, arguments);\n\n\t\t\tvar lines = [];\n\t\t\tlines = pushOrConcat(lines, beforeTitle);\n\t\t\tlines = pushOrConcat(lines, title);\n\t\t\tlines = pushOrConcat(lines, afterTitle);\n\n\t\t\treturn lines;\n\t\t},\n\n\t\t// Args are: (tooltipItem, data)\n\t\tgetBeforeBody: function() {\n\t\t\tvar lines = this._options.callbacks.beforeBody.apply(this, arguments);\n\t\t\treturn helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];\n\t\t},\n\n\t\t// Args are: (tooltipItem, data)\n\t\tgetBody: function(tooltipItems, data) {\n\t\t\tvar me = this;\n\t\t\tvar callbacks = me._options.callbacks;\n\t\t\tvar bodyItems = [];\n\n\t\t\thelpers.each(tooltipItems, function(tooltipItem) {\n\t\t\t\tvar bodyItem = {\n\t\t\t\t\tbefore: [],\n\t\t\t\t\tlines: [],\n\t\t\t\t\tafter: []\n\t\t\t\t};\n\t\t\t\tpushOrConcat(bodyItem.before, callbacks.beforeLabel.call(me, tooltipItem, data));\n\t\t\t\tpushOrConcat(bodyItem.lines, callbacks.label.call(me, tooltipItem, data));\n\t\t\t\tpushOrConcat(bodyItem.after, callbacks.afterLabel.call(me, tooltipItem, data));\n\n\t\t\t\tbodyItems.push(bodyItem);\n\t\t\t});\n\n\t\t\treturn bodyItems;\n\t\t},\n\n\t\t// Args are: (tooltipItem, data)\n\t\tgetAfterBody: function() {\n\t\t\tvar lines = this._options.callbacks.afterBody.apply(this, arguments);\n\t\t\treturn helpers.isArray(lines) ? lines : lines !== undefined ? [lines] : [];\n\t\t},\n\n\t\t// Get the footer and beforeFooter and afterFooter lines\n\t\t// Args are: (tooltipItem, data)\n\t\tgetFooter: function() {\n\t\t\tvar me = this;\n\t\t\tvar callbacks = me._options.callbacks;\n\n\t\t\tvar beforeFooter = callbacks.beforeFooter.apply(me, arguments);\n\t\t\tvar footer = callbacks.footer.apply(me, arguments);\n\t\t\tvar afterFooter = callbacks.afterFooter.apply(me, arguments);\n\n\t\t\tvar lines = [];\n\t\t\tlines = pushOrConcat(lines, beforeFooter);\n\t\t\tlines = pushOrConcat(lines, footer);\n\t\t\tlines = pushOrConcat(lines, afterFooter);\n\n\t\t\treturn lines;\n\t\t},\n\n\t\tupdate: function(changed) {\n\t\t\tvar me = this;\n\t\t\tvar opts = me._options;\n\n\t\t\t// Need to regenerate the model because its faster than using extend and it is necessary due to the optimization in Chart.Element.transition\n\t\t\t// that does _view = _model if ease === 1. This causes the 2nd tooltip update to set properties in both the view and model at the same time\n\t\t\t// which breaks any animations.\n\t\t\tvar existingModel = me._model;\n\t\t\tvar model = me._model = getBaseModel(opts);\n\t\t\tvar active = me._active;\n\n\t\t\tvar data = me._data;\n\n\t\t\t// In the case where active.length === 0 we need to keep these at existing values for good animations\n\t\t\tvar alignment = {\n\t\t\t\txAlign: existingModel.xAlign,\n\t\t\t\tyAlign: existingModel.yAlign\n\t\t\t};\n\t\t\tvar backgroundPoint = {\n\t\t\t\tx: existingModel.x,\n\t\t\t\ty: existingModel.y\n\t\t\t};\n\t\t\tvar tooltipSize = {\n\t\t\t\twidth: existingModel.width,\n\t\t\t\theight: existingModel.height\n\t\t\t};\n\t\t\tvar tooltipPosition = {\n\t\t\t\tx: existingModel.caretX,\n\t\t\t\ty: existingModel.caretY\n\t\t\t};\n\n\t\t\tvar i, len;\n\n\t\t\tif (active.length) {\n\t\t\t\tmodel.opacity = 1;\n\n\t\t\t\tvar labelColors = [];\n\t\t\t\tvar labelTextColors = [];\n\t\t\t\ttooltipPosition = Chart.Tooltip.positioners[opts.position].call(me, active, me._eventPosition);\n\n\t\t\t\tvar tooltipItems = [];\n\t\t\t\tfor (i = 0, len = active.length; i < len; ++i) {\n\t\t\t\t\ttooltipItems.push(createTooltipItem(active[i]));\n\t\t\t\t}\n\n\t\t\t\t// If the user provided a filter function, use it to modify the tooltip items\n\t\t\t\tif (opts.filter) {\n\t\t\t\t\ttooltipItems = tooltipItems.filter(function(a) {\n\t\t\t\t\t\treturn opts.filter(a, data);\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// If the user provided a sorting function, use it to modify the tooltip items\n\t\t\t\tif (opts.itemSort) {\n\t\t\t\t\ttooltipItems = tooltipItems.sort(function(a, b) {\n\t\t\t\t\t\treturn opts.itemSort(a, b, data);\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\t// Determine colors for boxes\n\t\t\t\thelpers.each(tooltipItems, function(tooltipItem) {\n\t\t\t\t\tlabelColors.push(opts.callbacks.labelColor.call(me, tooltipItem, me._chart));\n\t\t\t\t\tlabelTextColors.push(opts.callbacks.labelTextColor.call(me, tooltipItem, me._chart));\n\t\t\t\t});\n\n\n\t\t\t\t// Build the Text Lines\n\t\t\t\tmodel.title = me.getTitle(tooltipItems, data);\n\t\t\t\tmodel.beforeBody = me.getBeforeBody(tooltipItems, data);\n\t\t\t\tmodel.body = me.getBody(tooltipItems, data);\n\t\t\t\tmodel.afterBody = me.getAfterBody(tooltipItems, data);\n\t\t\t\tmodel.footer = me.getFooter(tooltipItems, data);\n\n\t\t\t\t// Initial positioning and colors\n\t\t\t\tmodel.x = Math.round(tooltipPosition.x);\n\t\t\t\tmodel.y = Math.round(tooltipPosition.y);\n\t\t\t\tmodel.caretPadding = opts.caretPadding;\n\t\t\t\tmodel.labelColors = labelColors;\n\t\t\t\tmodel.labelTextColors = labelTextColors;\n\n\t\t\t\t// data points\n\t\t\t\tmodel.dataPoints = tooltipItems;\n\n\t\t\t\t// We need to determine alignment of the tooltip\n\t\t\t\ttooltipSize = getTooltipSize(this, model);\n\t\t\t\talignment = determineAlignment(this, tooltipSize);\n\t\t\t\t// Final Size and Position\n\t\t\t\tbackgroundPoint = getBackgroundPoint(model, tooltipSize, alignment, me._chart);\n\t\t\t} else {\n\t\t\t\tmodel.opacity = 0;\n\t\t\t}\n\n\t\t\tmodel.xAlign = alignment.xAlign;\n\t\t\tmodel.yAlign = alignment.yAlign;\n\t\t\tmodel.x = backgroundPoint.x;\n\t\t\tmodel.y = backgroundPoint.y;\n\t\t\tmodel.width = tooltipSize.width;\n\t\t\tmodel.height = tooltipSize.height;\n\n\t\t\t// Point where the caret on the tooltip points to\n\t\t\tmodel.caretX = tooltipPosition.x;\n\t\t\tmodel.caretY = tooltipPosition.y;\n\n\t\t\tme._model = model;\n\n\t\t\tif (changed && opts.custom) {\n\t\t\t\topts.custom.call(me, model);\n\t\t\t}\n\n\t\t\treturn me;\n\t\t},\n\t\tdrawCaret: function(tooltipPoint, size) {\n\t\t\tvar ctx = this._chart.ctx;\n\t\t\tvar vm = this._view;\n\t\t\tvar caretPosition = this.getCaretPosition(tooltipPoint, size, vm);\n\n\t\t\tctx.lineTo(caretPosition.x1, caretPosition.y1);\n\t\t\tctx.lineTo(caretPosition.x2, caretPosition.y2);\n\t\t\tctx.lineTo(caretPosition.x3, caretPosition.y3);\n\t\t},\n\t\tgetCaretPosition: function(tooltipPoint, size, vm) {\n\t\t\tvar x1, x2, x3, y1, y2, y3;\n\t\t\tvar caretSize = vm.caretSize;\n\t\t\tvar cornerRadius = vm.cornerRadius;\n\t\t\tvar xAlign = vm.xAlign;\n\t\t\tvar yAlign = vm.yAlign;\n\t\t\tvar ptX = tooltipPoint.x;\n\t\t\tvar ptY = tooltipPoint.y;\n\t\t\tvar width = size.width;\n\t\t\tvar height = size.height;\n\n\t\t\tif (yAlign === 'center') {\n\t\t\t\ty2 = ptY + (height / 2);\n\n\t\t\t\tif (xAlign === 'left') {\n\t\t\t\t\tx1 = ptX;\n\t\t\t\t\tx2 = x1 - caretSize;\n\t\t\t\t\tx3 = x1;\n\n\t\t\t\t\ty1 = y2 + caretSize;\n\t\t\t\t\ty3 = y2 - caretSize;\n\t\t\t\t} else {\n\t\t\t\t\tx1 = ptX + width;\n\t\t\t\t\tx2 = x1 + caretSize;\n\t\t\t\t\tx3 = x1;\n\n\t\t\t\t\ty1 = y2 - caretSize;\n\t\t\t\t\ty3 = y2 + caretSize;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (xAlign === 'left') {\n\t\t\t\t\tx2 = ptX + cornerRadius + (caretSize);\n\t\t\t\t\tx1 = x2 - caretSize;\n\t\t\t\t\tx3 = x2 + caretSize;\n\t\t\t\t} else if (xAlign === 'right') {\n\t\t\t\t\tx2 = ptX + width - cornerRadius - caretSize;\n\t\t\t\t\tx1 = x2 - caretSize;\n\t\t\t\t\tx3 = x2 + caretSize;\n\t\t\t\t} else {\n\t\t\t\t\tx2 = vm.caretX;\n\t\t\t\t\tx1 = x2 - caretSize;\n\t\t\t\t\tx3 = x2 + caretSize;\n\t\t\t\t}\n\t\t\t\tif (yAlign === 'top') {\n\t\t\t\t\ty1 = ptY;\n\t\t\t\t\ty2 = y1 - caretSize;\n\t\t\t\t\ty3 = y1;\n\t\t\t\t} else {\n\t\t\t\t\ty1 = ptY + height;\n\t\t\t\t\ty2 = y1 + caretSize;\n\t\t\t\t\ty3 = y1;\n\t\t\t\t\t// invert drawing order\n\t\t\t\t\tvar tmp = x3;\n\t\t\t\t\tx3 = x1;\n\t\t\t\t\tx1 = tmp;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn {x1: x1, x2: x2, x3: x3, y1: y1, y2: y2, y3: y3};\n\t\t},\n\t\tdrawTitle: function(pt, vm, ctx, opacity) {\n\t\t\tvar title = vm.title;\n\n\t\t\tif (title.length) {\n\t\t\t\tctx.textAlign = vm._titleAlign;\n\t\t\t\tctx.textBaseline = 'top';\n\n\t\t\t\tvar titleFontSize = vm.titleFontSize;\n\t\t\t\tvar titleSpacing = vm.titleSpacing;\n\n\t\t\t\tctx.fillStyle = mergeOpacity(vm.titleFontColor, opacity);\n\t\t\t\tctx.font = helpers.fontString(titleFontSize, vm._titleFontStyle, vm._titleFontFamily);\n\n\t\t\t\tvar i, len;\n\t\t\t\tfor (i = 0, len = title.length; i < len; ++i) {\n\t\t\t\t\tctx.fillText(title[i], pt.x, pt.y);\n\t\t\t\t\tpt.y += titleFontSize + titleSpacing; // Line Height and spacing\n\n\t\t\t\t\tif (i + 1 === title.length) {\n\t\t\t\t\t\tpt.y += vm.titleMarginBottom - titleSpacing; // If Last, add margin, remove spacing\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tdrawBody: function(pt, vm, ctx, opacity) {\n\t\t\tvar bodyFontSize = vm.bodyFontSize;\n\t\t\tvar bodySpacing = vm.bodySpacing;\n\t\t\tvar body = vm.body;\n\n\t\t\tctx.textAlign = vm._bodyAlign;\n\t\t\tctx.textBaseline = 'top';\n\t\t\tctx.font = helpers.fontString(bodyFontSize, vm._bodyFontStyle, vm._bodyFontFamily);\n\n\t\t\t// Before Body\n\t\t\tvar xLinePadding = 0;\n\t\t\tvar fillLineOfText = function(line) {\n\t\t\t\tctx.fillText(line, pt.x + xLinePadding, pt.y);\n\t\t\t\tpt.y += bodyFontSize + bodySpacing;\n\t\t\t};\n\n\t\t\t// Before body lines\n\t\t\tctx.fillStyle = mergeOpacity(vm.bodyFontColor, opacity);\n\t\t\thelpers.each(vm.beforeBody, fillLineOfText);\n\n\t\t\tvar drawColorBoxes = vm.displayColors;\n\t\t\txLinePadding = drawColorBoxes ? (bodyFontSize + 2) : 0;\n\n\t\t\t// Draw body lines now\n\t\t\thelpers.each(body, function(bodyItem, i) {\n\t\t\t\tvar textColor = mergeOpacity(vm.labelTextColors[i], opacity);\n\t\t\t\tctx.fillStyle = textColor;\n\t\t\t\thelpers.each(bodyItem.before, fillLineOfText);\n\n\t\t\t\thelpers.each(bodyItem.lines, function(line) {\n\t\t\t\t\t// Draw Legend-like boxes if needed\n\t\t\t\t\tif (drawColorBoxes) {\n\t\t\t\t\t\t// Fill a white rect so that colours merge nicely if the opacity is < 1\n\t\t\t\t\t\tctx.fillStyle = mergeOpacity(vm.legendColorBackground, opacity);\n\t\t\t\t\t\tctx.fillRect(pt.x, pt.y, bodyFontSize, bodyFontSize);\n\n\t\t\t\t\t\t// Border\n\t\t\t\t\t\tctx.lineWidth = 1;\n\t\t\t\t\t\tctx.strokeStyle = mergeOpacity(vm.labelColors[i].borderColor, opacity);\n\t\t\t\t\t\tctx.strokeRect(pt.x, pt.y, bodyFontSize, bodyFontSize);\n\n\t\t\t\t\t\t// Inner square\n\t\t\t\t\t\tctx.fillStyle = mergeOpacity(vm.labelColors[i].backgroundColor, opacity);\n\t\t\t\t\t\tctx.fillRect(pt.x + 1, pt.y + 1, bodyFontSize - 2, bodyFontSize - 2);\n\t\t\t\t\t\tctx.fillStyle = textColor;\n\t\t\t\t\t}\n\n\t\t\t\t\tfillLineOfText(line);\n\t\t\t\t});\n\n\t\t\t\thelpers.each(bodyItem.after, fillLineOfText);\n\t\t\t});\n\n\t\t\t// Reset back to 0 for after body\n\t\t\txLinePadding = 0;\n\n\t\t\t// After body lines\n\t\t\thelpers.each(vm.afterBody, fillLineOfText);\n\t\t\tpt.y -= bodySpacing; // Remove last body spacing\n\t\t},\n\t\tdrawFooter: function(pt, vm, ctx, opacity) {\n\t\t\tvar footer = vm.footer;\n\n\t\t\tif (footer.length) {\n\t\t\t\tpt.y += vm.footerMarginTop;\n\n\t\t\t\tctx.textAlign = vm._footerAlign;\n\t\t\t\tctx.textBaseline = 'top';\n\n\t\t\t\tctx.fillStyle = mergeOpacity(vm.footerFontColor, opacity);\n\t\t\t\tctx.font = helpers.fontString(vm.footerFontSize, vm._footerFontStyle, vm._footerFontFamily);\n\n\t\t\t\thelpers.each(footer, function(line) {\n\t\t\t\t\tctx.fillText(line, pt.x, pt.y);\n\t\t\t\t\tpt.y += vm.footerFontSize + vm.footerSpacing;\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t\tdrawBackground: function(pt, vm, ctx, tooltipSize, opacity) {\n\t\t\tctx.fillStyle = mergeOpacity(vm.backgroundColor, opacity);\n\t\t\tctx.strokeStyle = mergeOpacity(vm.borderColor, opacity);\n\t\t\tctx.lineWidth = vm.borderWidth;\n\t\t\tvar xAlign = vm.xAlign;\n\t\t\tvar yAlign = vm.yAlign;\n\t\t\tvar x = pt.x;\n\t\t\tvar y = pt.y;\n\t\t\tvar width = tooltipSize.width;\n\t\t\tvar height = tooltipSize.height;\n\t\t\tvar radius = vm.cornerRadius;\n\n\t\t\tctx.beginPath();\n\t\t\tctx.moveTo(x + radius, y);\n\t\t\tif (yAlign === 'top') {\n\t\t\t\tthis.drawCaret(pt, tooltipSize);\n\t\t\t}\n\t\t\tctx.lineTo(x + width - radius, y);\n\t\t\tctx.quadraticCurveTo(x + width, y, x + width, y + radius);\n\t\t\tif (yAlign === 'center' && xAlign === 'right') {\n\t\t\t\tthis.drawCaret(pt, tooltipSize);\n\t\t\t}\n\t\t\tctx.lineTo(x + width, y + height - radius);\n\t\t\tctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);\n\t\t\tif (yAlign === 'bottom') {\n\t\t\t\tthis.drawCaret(pt, tooltipSize);\n\t\t\t}\n\t\t\tctx.lineTo(x + radius, y + height);\n\t\t\tctx.quadraticCurveTo(x, y + height, x, y + height - radius);\n\t\t\tif (yAlign === 'center' && xAlign === 'left') {\n\t\t\t\tthis.drawCaret(pt, tooltipSize);\n\t\t\t}\n\t\t\tctx.lineTo(x, y + radius);\n\t\t\tctx.quadraticCurveTo(x, y, x + radius, y);\n\t\t\tctx.closePath();\n\n\t\t\tctx.fill();\n\n\t\t\tif (vm.borderWidth > 0) {\n\t\t\t\tctx.stroke();\n\t\t\t}\n\t\t},\n\t\tdraw: function() {\n\t\t\tvar ctx = this._chart.ctx;\n\t\t\tvar vm = this._view;\n\n\t\t\tif (vm.opacity === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tvar tooltipSize = {\n\t\t\t\twidth: vm.width,\n\t\t\t\theight: vm.height\n\t\t\t};\n\t\t\tvar pt = {\n\t\t\t\tx: vm.x,\n\t\t\t\ty: vm.y\n\t\t\t};\n\n\t\t\t// IE11/Edge does not like very small opacities, so snap to 0\n\t\t\tvar opacity = Math.abs(vm.opacity < 1e-3) ? 0 : vm.opacity;\n\n\t\t\t// Truthy/falsey value for empty tooltip\n\t\t\tvar hasTooltipContent = vm.title.length || vm.beforeBody.length || vm.body.length || vm.afterBody.length || vm.footer.length;\n\n\t\t\tif (this._options.enabled && hasTooltipContent) {\n\t\t\t\t// Draw Background\n\t\t\t\tthis.drawBackground(pt, vm, ctx, tooltipSize, opacity);\n\n\t\t\t\t// Draw Title, Body, and Footer\n\t\t\t\tpt.x += vm.xPadding;\n\t\t\t\tpt.y += vm.yPadding;\n\n\t\t\t\t// Titles\n\t\t\t\tthis.drawTitle(pt, vm, ctx, opacity);\n\n\t\t\t\t// Body\n\t\t\t\tthis.drawBody(pt, vm, ctx, opacity);\n\n\t\t\t\t// Footer\n\t\t\t\tthis.drawFooter(pt, vm, ctx, opacity);\n\t\t\t}\n\t\t},\n\n\t\t/**\n\t\t * Handle an event\n\t\t * @private\n\t\t * @param {IEvent} event - The event to handle\n\t\t * @returns {Boolean} true if the tooltip changed\n\t\t */\n\t\thandleEvent: function(e) {\n\t\t\tvar me = this;\n\t\t\tvar options = me._options;\n\t\t\tvar changed = false;\n\n\t\t\tme._lastActive = me._lastActive || [];\n\n\t\t\t// Find Active Elements for tooltips\n\t\t\tif (e.type === 'mouseout') {\n\t\t\t\tme._active = [];\n\t\t\t} else {\n\t\t\t\tme._active = me._chart.getElementsAtEventForMode(e, options.mode, options);\n\t\t\t}\n\n\t\t\t// Remember Last Actives\n\t\t\tchanged = !helpers.arrayEquals(me._active, me._lastActive);\n\n\t\t\t// Only handle target event on tooltip change\n\t\t\tif (changed) {\n\t\t\t\tme._lastActive = me._active;\n\n\t\t\t\tif (options.enabled || options.custom) {\n\t\t\t\t\tme._eventPosition = {\n\t\t\t\t\t\tx: e.x,\n\t\t\t\t\t\ty: e.y\n\t\t\t\t\t};\n\n\t\t\t\t\tme.update(true);\n\t\t\t\t\tme.pivot();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn changed;\n\t\t}\n\t});\n\n\t/**\n\t * @namespace Chart.Tooltip.positioners\n\t */\n\tChart.Tooltip.positioners = {\n\t\t/**\n\t\t * Average mode places the tooltip at the average position of the elements shown\n\t\t * @function Chart.Tooltip.positioners.average\n\t\t * @param elements {ChartElement[]} the elements being displayed in the tooltip\n\t\t * @returns {Point} tooltip position\n\t\t */\n\t\taverage: function(elements) {\n\t\t\tif (!elements.length) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tvar i, len;\n\t\t\tvar x = 0;\n\t\t\tvar y = 0;\n\t\t\tvar count = 0;\n\n\t\t\tfor (i = 0, len = elements.length; i < len; ++i) {\n\t\t\t\tvar el = elements[i];\n\t\t\t\tif (el && el.hasValue()) {\n\t\t\t\t\tvar pos = el.tooltipPosition();\n\t\t\t\t\tx += pos.x;\n\t\t\t\t\ty += pos.y;\n\t\t\t\t\t++count;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tx: Math.round(x / count),\n\t\t\t\ty: Math.round(y / count)\n\t\t\t};\n\t\t},\n\n\t\t/**\n\t\t * Gets the tooltip position nearest of the item nearest to the event position\n\t\t * @function Chart.Tooltip.positioners.nearest\n\t\t * @param elements {Chart.Element[]} the tooltip elements\n\t\t * @param eventPosition {Point} the position of the event in canvas coordinates\n\t\t * @returns {Point} the tooltip position\n\t\t */\n\t\tnearest: function(elements, eventPosition) {\n\t\t\tvar x = eventPosition.x;\n\t\t\tvar y = eventPosition.y;\n\t\t\tvar minDistance = Number.POSITIVE_INFINITY;\n\t\t\tvar i, len, nearestElement;\n\n\t\t\tfor (i = 0, len = elements.length; i < len; ++i) {\n\t\t\t\tvar el = elements[i];\n\t\t\t\tif (el && el.hasValue()) {\n\t\t\t\t\tvar center = el.getCenterPoint();\n\t\t\t\t\tvar d = helpers.distanceBetweenPoints(eventPosition, center);\n\n\t\t\t\t\tif (d < minDistance) {\n\t\t\t\t\t\tminDistance = d;\n\t\t\t\t\t\tnearestElement = el;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (nearestElement) {\n\t\t\t\tvar tp = nearestElement.tooltipPosition();\n\t\t\t\tx = tp.x;\n\t\t\t\ty = tp.y;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tx: x,\n\t\t\t\ty: y\n\t\t\t};\n\t\t}\n\t};\n};\n\n},{\"25\":25,\"26\":26,\"45\":45}],36:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\nvar Element = require(26);\nvar helpers = require(45);\n\ndefaults._set('global', {\n\telements: {\n\t\tarc: {\n\t\t\tbackgroundColor: defaults.global.defaultColor,\n\t\t\tborderColor: '#fff',\n\t\t\tborderWidth: 2\n\t\t}\n\t}\n});\n\nmodule.exports = Element.extend({\n\tinLabelRange: function(mouseX) {\n\t\tvar vm = this._view;\n\n\t\tif (vm) {\n\t\t\treturn (Math.pow(mouseX - vm.x, 2) < Math.pow(vm.radius + vm.hoverRadius, 2));\n\t\t}\n\t\treturn false;\n\t},\n\n\tinRange: function(chartX, chartY) {\n\t\tvar vm = this._view;\n\n\t\tif (vm) {\n\t\t\tvar pointRelativePosition = helpers.getAngleFromPoint(vm, {x: chartX, y: chartY});\n\t\t\tvar\tangle = pointRelativePosition.angle;\n\t\t\tvar distance = pointRelativePosition.distance;\n\n\t\t\t// Sanitise angle range\n\t\t\tvar startAngle = vm.startAngle;\n\t\t\tvar endAngle = vm.endAngle;\n\t\t\twhile (endAngle < startAngle) {\n\t\t\t\tendAngle += 2.0 * Math.PI;\n\t\t\t}\n\t\t\twhile (angle > endAngle) {\n\t\t\t\tangle -= 2.0 * Math.PI;\n\t\t\t}\n\t\t\twhile (angle < startAngle) {\n\t\t\t\tangle += 2.0 * Math.PI;\n\t\t\t}\n\n\t\t\t// Check if within the range of the open/close angle\n\t\t\tvar betweenAngles = (angle >= startAngle && angle <= endAngle);\n\t\t\tvar withinRadius = (distance >= vm.innerRadius && distance <= vm.outerRadius);\n\n\t\t\treturn (betweenAngles && withinRadius);\n\t\t}\n\t\treturn false;\n\t},\n\n\tgetCenterPoint: function() {\n\t\tvar vm = this._view;\n\t\tvar halfAngle = (vm.startAngle + vm.endAngle) / 2;\n\t\tvar halfRadius = (vm.innerRadius + vm.outerRadius) / 2;\n\t\treturn {\n\t\t\tx: vm.x + Math.cos(halfAngle) * halfRadius,\n\t\t\ty: vm.y + Math.sin(halfAngle) * halfRadius\n\t\t};\n\t},\n\n\tgetArea: function() {\n\t\tvar vm = this._view;\n\t\treturn Math.PI * ((vm.endAngle - vm.startAngle) / (2 * Math.PI)) * (Math.pow(vm.outerRadius, 2) - Math.pow(vm.innerRadius, 2));\n\t},\n\n\ttooltipPosition: function() {\n\t\tvar vm = this._view;\n\t\tvar centreAngle = vm.startAngle + ((vm.endAngle - vm.startAngle) / 2);\n\t\tvar rangeFromCentre = (vm.outerRadius - vm.innerRadius) / 2 + vm.innerRadius;\n\n\t\treturn {\n\t\t\tx: vm.x + (Math.cos(centreAngle) * rangeFromCentre),\n\t\t\ty: vm.y + (Math.sin(centreAngle) * rangeFromCentre)\n\t\t};\n\t},\n\n\tdraw: function() {\n\t\tvar ctx = this._chart.ctx;\n\t\tvar vm = this._view;\n\t\tvar sA = vm.startAngle;\n\t\tvar eA = vm.endAngle;\n\n\t\tctx.beginPath();\n\n\t\tctx.arc(vm.x, vm.y, vm.outerRadius, sA, eA);\n\t\tctx.arc(vm.x, vm.y, vm.innerRadius, eA, sA, true);\n\n\t\tctx.closePath();\n\t\tctx.strokeStyle = vm.borderColor;\n\t\tctx.lineWidth = vm.borderWidth;\n\n\t\tctx.fillStyle = vm.backgroundColor;\n\n\t\tctx.fill();\n\t\tctx.lineJoin = 'bevel';\n\n\t\tif (vm.borderWidth) {\n\t\t\tctx.stroke();\n\t\t}\n\t}\n});\n\n},{\"25\":25,\"26\":26,\"45\":45}],37:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\nvar Element = require(26);\nvar helpers = require(45);\n\nvar globalDefaults = defaults.global;\n\ndefaults._set('global', {\n\telements: {\n\t\tline: {\n\t\t\ttension: 0.4,\n\t\t\tbackgroundColor: globalDefaults.defaultColor,\n\t\t\tborderWidth: 3,\n\t\t\tborderColor: globalDefaults.defaultColor,\n\t\t\tborderCapStyle: 'butt',\n\t\t\tborderDash: [],\n\t\t\tborderDashOffset: 0.0,\n\t\t\tborderJoinStyle: 'miter',\n\t\t\tcapBezierPoints: true,\n\t\t\tfill: true, // do we fill in the area between the line and its base axis\n\t\t}\n\t}\n});\n\nmodule.exports = Element.extend({\n\tdraw: function() {\n\t\tvar me = this;\n\t\tvar vm = me._view;\n\t\tvar ctx = me._chart.ctx;\n\t\tvar spanGaps = vm.spanGaps;\n\t\tvar points = me._children.slice(); // clone array\n\t\tvar globalOptionLineElements = globalDefaults.elements.line;\n\t\tvar lastDrawnIndex = -1;\n\t\tvar index, current, previous, currentVM;\n\n\t\t// If we are looping, adding the first point again\n\t\tif (me._loop && points.length) {\n\t\t\tpoints.push(points[0]);\n\t\t}\n\n\t\tctx.save();\n\n\t\t// Stroke Line Options\n\t\tctx.lineCap = vm.borderCapStyle || globalOptionLineElements.borderCapStyle;\n\n\t\t// IE 9 and 10 do not support line dash\n\t\tif (ctx.setLineDash) {\n\t\t\tctx.setLineDash(vm.borderDash || globalOptionLineElements.borderDash);\n\t\t}\n\n\t\tctx.lineDashOffset = vm.borderDashOffset || globalOptionLineElements.borderDashOffset;\n\t\tctx.lineJoin = vm.borderJoinStyle || globalOptionLineElements.borderJoinStyle;\n\t\tctx.lineWidth = vm.borderWidth || globalOptionLineElements.borderWidth;\n\t\tctx.strokeStyle = vm.borderColor || globalDefaults.defaultColor;\n\n\t\t// Stroke Line\n\t\tctx.beginPath();\n\t\tlastDrawnIndex = -1;\n\n\t\tfor (index = 0; index < points.length; ++index) {\n\t\t\tcurrent = points[index];\n\t\t\tprevious = helpers.previousItem(points, index);\n\t\t\tcurrentVM = current._view;\n\n\t\t\t// First point moves to it's starting position no matter what\n\t\t\tif (index === 0) {\n\t\t\t\tif (!currentVM.skip) {\n\t\t\t\t\tctx.moveTo(currentVM.x, currentVM.y);\n\t\t\t\t\tlastDrawnIndex = index;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tprevious = lastDrawnIndex === -1 ? previous : points[lastDrawnIndex];\n\n\t\t\t\tif (!currentVM.skip) {\n\t\t\t\t\tif ((lastDrawnIndex !== (index - 1) && !spanGaps) || lastDrawnIndex === -1) {\n\t\t\t\t\t\t// There was a gap and this is the first point after the gap\n\t\t\t\t\t\tctx.moveTo(currentVM.x, currentVM.y);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// Line to next point\n\t\t\t\t\t\thelpers.canvas.lineTo(ctx, previous._view, current._view);\n\t\t\t\t\t}\n\t\t\t\t\tlastDrawnIndex = index;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tctx.stroke();\n\t\tctx.restore();\n\t}\n});\n\n},{\"25\":25,\"26\":26,\"45\":45}],38:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\nvar Element = require(26);\nvar helpers = require(45);\n\nvar defaultColor = defaults.global.defaultColor;\n\ndefaults._set('global', {\n\telements: {\n\t\tpoint: {\n\t\t\tradius: 3,\n\t\t\tpointStyle: 'circle',\n\t\t\tbackgroundColor: defaultColor,\n\t\t\tborderColor: defaultColor,\n\t\t\tborderWidth: 1,\n\t\t\t// Hover\n\t\t\thitRadius: 1,\n\t\t\thoverRadius: 4,\n\t\t\thoverBorderWidth: 1\n\t\t}\n\t}\n});\n\nfunction xRange(mouseX) {\n\tvar vm = this._view;\n\treturn vm ? (Math.abs(mouseX - vm.x) < vm.radius + vm.hitRadius) : false;\n}\n\nfunction yRange(mouseY) {\n\tvar vm = this._view;\n\treturn vm ? (Math.abs(mouseY - vm.y) < vm.radius + vm.hitRadius) : false;\n}\n\nmodule.exports = Element.extend({\n\tinRange: function(mouseX, mouseY) {\n\t\tvar vm = this._view;\n\t\treturn vm ? ((Math.pow(mouseX - vm.x, 2) + Math.pow(mouseY - vm.y, 2)) < Math.pow(vm.hitRadius + vm.radius, 2)) : false;\n\t},\n\n\tinLabelRange: xRange,\n\tinXRange: xRange,\n\tinYRange: yRange,\n\n\tgetCenterPoint: function() {\n\t\tvar vm = this._view;\n\t\treturn {\n\t\t\tx: vm.x,\n\t\t\ty: vm.y\n\t\t};\n\t},\n\n\tgetArea: function() {\n\t\treturn Math.PI * Math.pow(this._view.radius, 2);\n\t},\n\n\ttooltipPosition: function() {\n\t\tvar vm = this._view;\n\t\treturn {\n\t\t\tx: vm.x,\n\t\t\ty: vm.y,\n\t\t\tpadding: vm.radius + vm.borderWidth\n\t\t};\n\t},\n\n\tdraw: function(chartArea) {\n\t\tvar vm = this._view;\n\t\tvar model = this._model;\n\t\tvar ctx = this._chart.ctx;\n\t\tvar pointStyle = vm.pointStyle;\n\t\tvar radius = vm.radius;\n\t\tvar x = vm.x;\n\t\tvar y = vm.y;\n\t\tvar color = helpers.color;\n\t\tvar errMargin = 1.01; // 1.01 is margin for Accumulated error. (Especially Edge, IE.)\n\t\tvar ratio = 0;\n\n\t\tif (vm.skip) {\n\t\t\treturn;\n\t\t}\n\n\t\tctx.strokeStyle = vm.borderColor || defaultColor;\n\t\tctx.lineWidth = helpers.valueOrDefault(vm.borderWidth, defaults.global.elements.point.borderWidth);\n\t\tctx.fillStyle = vm.backgroundColor || defaultColor;\n\n\t\t// Cliping for Points.\n\t\t// going out from inner charArea?\n\t\tif ((chartArea !== undefined) && ((model.x < chartArea.left) || (chartArea.right * errMargin < model.x) || (model.y < chartArea.top) || (chartArea.bottom * errMargin < model.y))) {\n\t\t\t// Point fade out\n\t\t\tif (model.x < chartArea.left) {\n\t\t\t\tratio = (x - model.x) / (chartArea.left - model.x);\n\t\t\t} else if (chartArea.right * errMargin < model.x) {\n\t\t\t\tratio = (model.x - x) / (model.x - chartArea.right);\n\t\t\t} else if (model.y < chartArea.top) {\n\t\t\t\tratio = (y - model.y) / (chartArea.top - model.y);\n\t\t\t} else if (chartArea.bottom * errMargin < model.y) {\n\t\t\t\tratio = (model.y - y) / (model.y - chartArea.bottom);\n\t\t\t}\n\t\t\tratio = Math.round(ratio * 100) / 100;\n\t\t\tctx.strokeStyle = color(ctx.strokeStyle).alpha(ratio).rgbString();\n\t\t\tctx.fillStyle = color(ctx.fillStyle).alpha(ratio).rgbString();\n\t\t}\n\n\t\thelpers.canvas.drawPoint(ctx, pointStyle, radius, x, y);\n\t}\n});\n\n},{\"25\":25,\"26\":26,\"45\":45}],39:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\nvar Element = require(26);\n\ndefaults._set('global', {\n\telements: {\n\t\trectangle: {\n\t\t\tbackgroundColor: defaults.global.defaultColor,\n\t\t\tborderColor: defaults.global.defaultColor,\n\t\t\tborderSkipped: 'bottom',\n\t\t\tborderWidth: 0\n\t\t}\n\t}\n});\n\nfunction isVertical(bar) {\n\treturn bar._view.width !== undefined;\n}\n\n/**\n * Helper function to get the bounds of the bar regardless of the orientation\n * @param bar {Chart.Element.Rectangle} the bar\n * @return {Bounds} bounds of the bar\n * @private\n */\nfunction getBarBounds(bar) {\n\tvar vm = bar._view;\n\tvar x1, x2, y1, y2;\n\n\tif (isVertical(bar)) {\n\t\t// vertical\n\t\tvar halfWidth = vm.width / 2;\n\t\tx1 = vm.x - halfWidth;\n\t\tx2 = vm.x + halfWidth;\n\t\ty1 = Math.min(vm.y, vm.base);\n\t\ty2 = Math.max(vm.y, vm.base);\n\t} else {\n\t\t// horizontal bar\n\t\tvar halfHeight = vm.height / 2;\n\t\tx1 = Math.min(vm.x, vm.base);\n\t\tx2 = Math.max(vm.x, vm.base);\n\t\ty1 = vm.y - halfHeight;\n\t\ty2 = vm.y + halfHeight;\n\t}\n\n\treturn {\n\t\tleft: x1,\n\t\ttop: y1,\n\t\tright: x2,\n\t\tbottom: y2\n\t};\n}\n\nmodule.exports = Element.extend({\n\tdraw: function() {\n\t\tvar ctx = this._chart.ctx;\n\t\tvar vm = this._view;\n\t\tvar left, right, top, bottom, signX, signY, borderSkipped;\n\t\tvar borderWidth = vm.borderWidth;\n\n\t\tif (!vm.horizontal) {\n\t\t\t// bar\n\t\t\tleft = vm.x - vm.width / 2;\n\t\t\tright = vm.x + vm.width / 2;\n\t\t\ttop = vm.y;\n\t\t\tbottom = vm.base;\n\t\t\tsignX = 1;\n\t\t\tsignY = bottom > top ? 1 : -1;\n\t\t\tborderSkipped = vm.borderSkipped || 'bottom';\n\t\t} else {\n\t\t\t// horizontal bar\n\t\t\tleft = vm.base;\n\t\t\tright = vm.x;\n\t\t\ttop = vm.y - vm.height / 2;\n\t\t\tbottom = vm.y + vm.height / 2;\n\t\t\tsignX = right > left ? 1 : -1;\n\t\t\tsignY = 1;\n\t\t\tborderSkipped = vm.borderSkipped || 'left';\n\t\t}\n\n\t\t// Canvas doesn't allow us to stroke inside the width so we can\n\t\t// adjust the sizes to fit if we're setting a stroke on the line\n\t\tif (borderWidth) {\n\t\t\t// borderWidth should be less than bar width and bar height.\n\t\t\tvar barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom));\n\t\t\tborderWidth = borderWidth > barSize ? barSize : borderWidth;\n\t\t\tvar halfStroke = borderWidth / 2;\n\t\t\t// Adjust borderWidth when bar top position is near vm.base(zero).\n\t\t\tvar borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0);\n\t\t\tvar borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0);\n\t\t\tvar borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0);\n\t\t\tvar borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0);\n\t\t\t// not become a vertical line?\n\t\t\tif (borderLeft !== borderRight) {\n\t\t\t\ttop = borderTop;\n\t\t\t\tbottom = borderBottom;\n\t\t\t}\n\t\t\t// not become a horizontal line?\n\t\t\tif (borderTop !== borderBottom) {\n\t\t\t\tleft = borderLeft;\n\t\t\t\tright = borderRight;\n\t\t\t}\n\t\t}\n\n\t\tctx.beginPath();\n\t\tctx.fillStyle = vm.backgroundColor;\n\t\tctx.strokeStyle = vm.borderColor;\n\t\tctx.lineWidth = borderWidth;\n\n\t\t// Corner points, from bottom-left to bottom-right clockwise\n\t\t// | 1 2 |\n\t\t// | 0 3 |\n\t\tvar corners = [\n\t\t\t[left, bottom],\n\t\t\t[left, top],\n\t\t\t[right, top],\n\t\t\t[right, bottom]\n\t\t];\n\n\t\t// Find first (starting) corner with fallback to 'bottom'\n\t\tvar borders = ['bottom', 'left', 'top', 'right'];\n\t\tvar startCorner = borders.indexOf(borderSkipped, 0);\n\t\tif (startCorner === -1) {\n\t\t\tstartCorner = 0;\n\t\t}\n\n\t\tfunction cornerAt(index) {\n\t\t\treturn corners[(startCorner + index) % 4];\n\t\t}\n\n\t\t// Draw rectangle from 'startCorner'\n\t\tvar corner = cornerAt(0);\n\t\tctx.moveTo(corner[0], corner[1]);\n\n\t\tfor (var i = 1; i < 4; i++) {\n\t\t\tcorner = cornerAt(i);\n\t\t\tctx.lineTo(corner[0], corner[1]);\n\t\t}\n\n\t\tctx.fill();\n\t\tif (borderWidth) {\n\t\t\tctx.stroke();\n\t\t}\n\t},\n\n\theight: function() {\n\t\tvar vm = this._view;\n\t\treturn vm.base - vm.y;\n\t},\n\n\tinRange: function(mouseX, mouseY) {\n\t\tvar inRange = false;\n\n\t\tif (this._view) {\n\t\t\tvar bounds = getBarBounds(this);\n\t\t\tinRange = mouseX >= bounds.left && mouseX <= bounds.right && mouseY >= bounds.top && mouseY <= bounds.bottom;\n\t\t}\n\n\t\treturn inRange;\n\t},\n\n\tinLabelRange: function(mouseX, mouseY) {\n\t\tvar me = this;\n\t\tif (!me._view) {\n\t\t\treturn false;\n\t\t}\n\n\t\tvar inRange = false;\n\t\tvar bounds = getBarBounds(me);\n\n\t\tif (isVertical(me)) {\n\t\t\tinRange = mouseX >= bounds.left && mouseX <= bounds.right;\n\t\t} else {\n\t\t\tinRange = mouseY >= bounds.top && mouseY <= bounds.bottom;\n\t\t}\n\n\t\treturn inRange;\n\t},\n\n\tinXRange: function(mouseX) {\n\t\tvar bounds = getBarBounds(this);\n\t\treturn mouseX >= bounds.left && mouseX <= bounds.right;\n\t},\n\n\tinYRange: function(mouseY) {\n\t\tvar bounds = getBarBounds(this);\n\t\treturn mouseY >= bounds.top && mouseY <= bounds.bottom;\n\t},\n\n\tgetCenterPoint: function() {\n\t\tvar vm = this._view;\n\t\tvar x, y;\n\t\tif (isVertical(this)) {\n\t\t\tx = vm.x;\n\t\t\ty = (vm.y + vm.base) / 2;\n\t\t} else {\n\t\t\tx = (vm.x + vm.base) / 2;\n\t\t\ty = vm.y;\n\t\t}\n\n\t\treturn {x: x, y: y};\n\t},\n\n\tgetArea: function() {\n\t\tvar vm = this._view;\n\t\treturn vm.width * Math.abs(vm.y - vm.base);\n\t},\n\n\ttooltipPosition: function() {\n\t\tvar vm = this._view;\n\t\treturn {\n\t\t\tx: vm.x,\n\t\t\ty: vm.y\n\t\t};\n\t}\n});\n\n},{\"25\":25,\"26\":26}],40:[function(require,module,exports){\n'use strict';\n\nmodule.exports = {};\nmodule.exports.Arc = require(36);\nmodule.exports.Line = require(37);\nmodule.exports.Point = require(38);\nmodule.exports.Rectangle = require(39);\n\n},{\"36\":36,\"37\":37,\"38\":38,\"39\":39}],41:[function(require,module,exports){\n'use strict';\n\nvar helpers = require(42);\n\n/**\n * @namespace Chart.helpers.canvas\n */\nvar exports = module.exports = {\n\t/**\n\t * Clears the entire canvas associated to the given `chart`.\n\t * @param {Chart} chart - The chart for which to clear the canvas.\n\t */\n\tclear: function(chart) {\n\t\tchart.ctx.clearRect(0, 0, chart.width, chart.height);\n\t},\n\n\t/**\n\t * Creates a \"path\" for a rectangle with rounded corners at position (x, y) with a\n\t * given size (width, height) and the same `radius` for all corners.\n\t * @param {CanvasRenderingContext2D} ctx - The canvas 2D Context.\n\t * @param {Number} x - The x axis of the coordinate for the rectangle starting point.\n\t * @param {Number} y - The y axis of the coordinate for the rectangle starting point.\n\t * @param {Number} width - The rectangle's width.\n\t * @param {Number} height - The rectangle's height.\n\t * @param {Number} radius - The rounded amount (in pixels) for the four corners.\n\t * @todo handle `radius` as top-left, top-right, bottom-right, bottom-left array/object?\n\t */\n\troundedRect: function(ctx, x, y, width, height, radius) {\n\t\tif (radius) {\n\t\t\tvar rx = Math.min(radius, width / 2);\n\t\t\tvar ry = Math.min(radius, height / 2);\n\n\t\t\tctx.moveTo(x + rx, y);\n\t\t\tctx.lineTo(x + width - rx, y);\n\t\t\tctx.quadraticCurveTo(x + width, y, x + width, y + ry);\n\t\t\tctx.lineTo(x + width, y + height - ry);\n\t\t\tctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height);\n\t\t\tctx.lineTo(x + rx, y + height);\n\t\t\tctx.quadraticCurveTo(x, y + height, x, y + height - ry);\n\t\t\tctx.lineTo(x, y + ry);\n\t\t\tctx.quadraticCurveTo(x, y, x + rx, y);\n\t\t} else {\n\t\t\tctx.rect(x, y, width, height);\n\t\t}\n\t},\n\n\tdrawPoint: function(ctx, style, radius, x, y) {\n\t\tvar type, edgeLength, xOffset, yOffset, height, size;\n\n\t\tif (style && typeof style === 'object') {\n\t\t\ttype = style.toString();\n\t\t\tif (type === '[object HTMLImageElement]' || type === '[object HTMLCanvasElement]') {\n\t\t\t\tctx.drawImage(style, x - style.width / 2, y - style.height / 2, style.width, style.height);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tif (isNaN(radius) || radius <= 0) {\n\t\t\treturn;\n\t\t}\n\n\t\tswitch (style) {\n\t\t// Default includes circle\n\t\tdefault:\n\t\t\tctx.beginPath();\n\t\t\tctx.arc(x, y, radius, 0, Math.PI * 2);\n\t\t\tctx.closePath();\n\t\t\tctx.fill();\n\t\t\tbreak;\n\t\tcase 'triangle':\n\t\t\tctx.beginPath();\n\t\t\tedgeLength = 3 * radius / Math.sqrt(3);\n\t\t\theight = edgeLength * Math.sqrt(3) / 2;\n\t\t\tctx.moveTo(x - edgeLength / 2, y + height / 3);\n\t\t\tctx.lineTo(x + edgeLength / 2, y + height / 3);\n\t\t\tctx.lineTo(x, y - 2 * height / 3);\n\t\t\tctx.closePath();\n\t\t\tctx.fill();\n\t\t\tbreak;\n\t\tcase 'rect':\n\t\t\tsize = 1 / Math.SQRT2 * radius;\n\t\t\tctx.beginPath();\n\t\t\tctx.fillRect(x - size, y - size, 2 * size, 2 * size);\n\t\t\tctx.strokeRect(x - size, y - size, 2 * size, 2 * size);\n\t\t\tbreak;\n\t\tcase 'rectRounded':\n\t\t\tvar offset = radius / Math.SQRT2;\n\t\t\tvar leftX = x - offset;\n\t\t\tvar topY = y - offset;\n\t\t\tvar sideSize = Math.SQRT2 * radius;\n\t\t\tctx.beginPath();\n\t\t\tthis.roundedRect(ctx, leftX, topY, sideSize, sideSize, radius / 2);\n\t\t\tctx.closePath();\n\t\t\tctx.fill();\n\t\t\tbreak;\n\t\tcase 'rectRot':\n\t\t\tsize = 1 / Math.SQRT2 * radius;\n\t\t\tctx.beginPath();\n\t\t\tctx.moveTo(x - size, y);\n\t\t\tctx.lineTo(x, y + size);\n\t\t\tctx.lineTo(x + size, y);\n\t\t\tctx.lineTo(x, y - size);\n\t\t\tctx.closePath();\n\t\t\tctx.fill();\n\t\t\tbreak;\n\t\tcase 'cross':\n\t\t\tctx.beginPath();\n\t\t\tctx.moveTo(x, y + radius);\n\t\t\tctx.lineTo(x, y - radius);\n\t\t\tctx.moveTo(x - radius, y);\n\t\t\tctx.lineTo(x + radius, y);\n\t\t\tctx.closePath();\n\t\t\tbreak;\n\t\tcase 'crossRot':\n\t\t\tctx.beginPath();\n\t\t\txOffset = Math.cos(Math.PI / 4) * radius;\n\t\t\tyOffset = Math.sin(Math.PI / 4) * radius;\n\t\t\tctx.moveTo(x - xOffset, y - yOffset);\n\t\t\tctx.lineTo(x + xOffset, y + yOffset);\n\t\t\tctx.moveTo(x - xOffset, y + yOffset);\n\t\t\tctx.lineTo(x + xOffset, y - yOffset);\n\t\t\tctx.closePath();\n\t\t\tbreak;\n\t\tcase 'star':\n\t\t\tctx.beginPath();\n\t\t\tctx.moveTo(x, y + radius);\n\t\t\tctx.lineTo(x, y - radius);\n\t\t\tctx.moveTo(x - radius, y);\n\t\t\tctx.lineTo(x + radius, y);\n\t\t\txOffset = Math.cos(Math.PI / 4) * radius;\n\t\t\tyOffset = Math.sin(Math.PI / 4) * radius;\n\t\t\tctx.moveTo(x - xOffset, y - yOffset);\n\t\t\tctx.lineTo(x + xOffset, y + yOffset);\n\t\t\tctx.moveTo(x - xOffset, y + yOffset);\n\t\t\tctx.lineTo(x + xOffset, y - yOffset);\n\t\t\tctx.closePath();\n\t\t\tbreak;\n\t\tcase 'line':\n\t\t\tctx.beginPath();\n\t\t\tctx.moveTo(x - radius, y);\n\t\t\tctx.lineTo(x + radius, y);\n\t\t\tctx.closePath();\n\t\t\tbreak;\n\t\tcase 'dash':\n\t\t\tctx.beginPath();\n\t\t\tctx.moveTo(x, y);\n\t\t\tctx.lineTo(x + radius, y);\n\t\t\tctx.closePath();\n\t\t\tbreak;\n\t\t}\n\n\t\tctx.stroke();\n\t},\n\n\tclipArea: function(ctx, area) {\n\t\tctx.save();\n\t\tctx.beginPath();\n\t\tctx.rect(area.left, area.top, area.right - area.left, area.bottom - area.top);\n\t\tctx.clip();\n\t},\n\n\tunclipArea: function(ctx) {\n\t\tctx.restore();\n\t},\n\n\tlineTo: function(ctx, previous, target, flip) {\n\t\tif (target.steppedLine) {\n\t\t\tif ((target.steppedLine === 'after' && !flip) || (target.steppedLine !== 'after' && flip)) {\n\t\t\t\tctx.lineTo(previous.x, target.y);\n\t\t\t} else {\n\t\t\t\tctx.lineTo(target.x, previous.y);\n\t\t\t}\n\t\t\tctx.lineTo(target.x, target.y);\n\t\t\treturn;\n\t\t}\n\n\t\tif (!target.tension) {\n\t\t\tctx.lineTo(target.x, target.y);\n\t\t\treturn;\n\t\t}\n\n\t\tctx.bezierCurveTo(\n\t\t\tflip ? previous.controlPointPreviousX : previous.controlPointNextX,\n\t\t\tflip ? previous.controlPointPreviousY : previous.controlPointNextY,\n\t\t\tflip ? target.controlPointNextX : target.controlPointPreviousX,\n\t\t\tflip ? target.controlPointNextY : target.controlPointPreviousY,\n\t\t\ttarget.x,\n\t\t\ttarget.y);\n\t}\n};\n\n// DEPRECATIONS\n\n/**\n * Provided for backward compatibility, use Chart.helpers.canvas.clear instead.\n * @namespace Chart.helpers.clear\n * @deprecated since version 2.7.0\n * @todo remove at version 3\n * @private\n */\nhelpers.clear = exports.clear;\n\n/**\n * Provided for backward compatibility, use Chart.helpers.canvas.roundedRect instead.\n * @namespace Chart.helpers.drawRoundedRectangle\n * @deprecated since version 2.7.0\n * @todo remove at version 3\n * @private\n */\nhelpers.drawRoundedRectangle = function(ctx) {\n\tctx.beginPath();\n\texports.roundedRect.apply(exports, arguments);\n\tctx.closePath();\n};\n\n},{\"42\":42}],42:[function(require,module,exports){\n'use strict';\n\n/**\n * @namespace Chart.helpers\n */\nvar helpers = {\n\t/**\n\t * An empty function that can be used, for example, for optional callback.\n\t */\n\tnoop: function() {},\n\n\t/**\n\t * Returns a unique id, sequentially generated from a global variable.\n\t * @returns {Number}\n\t * @function\n\t */\n\tuid: (function() {\n\t\tvar id = 0;\n\t\treturn function() {\n\t\t\treturn id++;\n\t\t};\n\t}()),\n\n\t/**\n\t * Returns true if `value` is neither null nor undefined, else returns false.\n\t * @param {*} value - The value to test.\n\t * @returns {Boolean}\n\t * @since 2.7.0\n\t */\n\tisNullOrUndef: function(value) {\n\t\treturn value === null || typeof value === 'undefined';\n\t},\n\n\t/**\n\t * Returns true if `value` is an array, else returns false.\n\t * @param {*} value - The value to test.\n\t * @returns {Boolean}\n\t * @function\n\t */\n\tisArray: Array.isArray ? Array.isArray : function(value) {\n\t\treturn Object.prototype.toString.call(value) === '[object Array]';\n\t},\n\n\t/**\n\t * Returns true if `value` is an object (excluding null), else returns false.\n\t * @param {*} value - The value to test.\n\t * @returns {Boolean}\n\t * @since 2.7.0\n\t */\n\tisObject: function(value) {\n\t\treturn value !== null && Object.prototype.toString.call(value) === '[object Object]';\n\t},\n\n\t/**\n\t * Returns `value` if defined, else returns `defaultValue`.\n\t * @param {*} value - The value to return if defined.\n\t * @param {*} defaultValue - The value to return if `value` is undefined.\n\t * @returns {*}\n\t */\n\tvalueOrDefault: function(value, defaultValue) {\n\t\treturn typeof value === 'undefined' ? defaultValue : value;\n\t},\n\n\t/**\n\t * Returns value at the given `index` in array if defined, else returns `defaultValue`.\n\t * @param {Array} value - The array to lookup for value at `index`.\n\t * @param {Number} index - The index in `value` to lookup for value.\n\t * @param {*} defaultValue - The value to return if `value[index]` is undefined.\n\t * @returns {*}\n\t */\n\tvalueAtIndexOrDefault: function(value, index, defaultValue) {\n\t\treturn helpers.valueOrDefault(helpers.isArray(value) ? value[index] : value, defaultValue);\n\t},\n\n\t/**\n\t * Calls `fn` with the given `args` in the scope defined by `thisArg` and returns the\n\t * value returned by `fn`. If `fn` is not a function, this method returns undefined.\n\t * @param {Function} fn - The function to call.\n\t * @param {Array|undefined|null} args - The arguments with which `fn` should be called.\n\t * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.\n\t * @returns {*}\n\t */\n\tcallback: function(fn, args, thisArg) {\n\t\tif (fn && typeof fn.call === 'function') {\n\t\t\treturn fn.apply(thisArg, args);\n\t\t}\n\t},\n\n\t/**\n\t * Note(SB) for performance sake, this method should only be used when loopable type\n\t * is unknown or in none intensive code (not called often and small loopable). Else\n\t * it's preferable to use a regular for() loop and save extra function calls.\n\t * @param {Object|Array} loopable - The object or array to be iterated.\n\t * @param {Function} fn - The function to call for each item.\n\t * @param {Object} [thisArg] - The value of `this` provided for the call to `fn`.\n\t * @param {Boolean} [reverse] - If true, iterates backward on the loopable.\n\t */\n\teach: function(loopable, fn, thisArg, reverse) {\n\t\tvar i, len, keys;\n\t\tif (helpers.isArray(loopable)) {\n\t\t\tlen = loopable.length;\n\t\t\tif (reverse) {\n\t\t\t\tfor (i = len - 1; i >= 0; i--) {\n\t\t\t\t\tfn.call(thisArg, loopable[i], i);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (i = 0; i < len; i++) {\n\t\t\t\t\tfn.call(thisArg, loopable[i], i);\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (helpers.isObject(loopable)) {\n\t\t\tkeys = Object.keys(loopable);\n\t\t\tlen = keys.length;\n\t\t\tfor (i = 0; i < len; i++) {\n\t\t\t\tfn.call(thisArg, loopable[keys[i]], keys[i]);\n\t\t\t}\n\t\t}\n\t},\n\n\t/**\n\t * Returns true if the `a0` and `a1` arrays have the same content, else returns false.\n\t * @see http://stackoverflow.com/a/14853974\n\t * @param {Array} a0 - The array to compare\n\t * @param {Array} a1 - The array to compare\n\t * @returns {Boolean}\n\t */\n\tarrayEquals: function(a0, a1) {\n\t\tvar i, ilen, v0, v1;\n\n\t\tif (!a0 || !a1 || a0.length !== a1.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (i = 0, ilen = a0.length; i < ilen; ++i) {\n\t\t\tv0 = a0[i];\n\t\t\tv1 = a1[i];\n\n\t\t\tif (v0 instanceof Array && v1 instanceof Array) {\n\t\t\t\tif (!helpers.arrayEquals(v0, v1)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t} else if (v0 !== v1) {\n\t\t\t\t// NOTE: two different object instances will never be equal: {x:20} != {x:20}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn true;\n\t},\n\n\t/**\n\t * Returns a deep copy of `source` without keeping references on objects and arrays.\n\t * @param {*} source - The value to clone.\n\t * @returns {*}\n\t */\n\tclone: function(source) {\n\t\tif (helpers.isArray(source)) {\n\t\t\treturn source.map(helpers.clone);\n\t\t}\n\n\t\tif (helpers.isObject(source)) {\n\t\t\tvar target = {};\n\t\t\tvar keys = Object.keys(source);\n\t\t\tvar klen = keys.length;\n\t\t\tvar k = 0;\n\n\t\t\tfor (; k < klen; ++k) {\n\t\t\t\ttarget[keys[k]] = helpers.clone(source[keys[k]]);\n\t\t\t}\n\n\t\t\treturn target;\n\t\t}\n\n\t\treturn source;\n\t},\n\n\t/**\n\t * The default merger when Chart.helpers.merge is called without merger option.\n\t * Note(SB): this method is also used by configMerge and scaleMerge as fallback.\n\t * @private\n\t */\n\t_merger: function(key, target, source, options) {\n\t\tvar tval = target[key];\n\t\tvar sval = source[key];\n\n\t\tif (helpers.isObject(tval) && helpers.isObject(sval)) {\n\t\t\thelpers.merge(tval, sval, options);\n\t\t} else {\n\t\t\ttarget[key] = helpers.clone(sval);\n\t\t}\n\t},\n\n\t/**\n\t * Merges source[key] in target[key] only if target[key] is undefined.\n\t * @private\n\t */\n\t_mergerIf: function(key, target, source) {\n\t\tvar tval = target[key];\n\t\tvar sval = source[key];\n\n\t\tif (helpers.isObject(tval) && helpers.isObject(sval)) {\n\t\t\thelpers.mergeIf(tval, sval);\n\t\t} else if (!target.hasOwnProperty(key)) {\n\t\t\ttarget[key] = helpers.clone(sval);\n\t\t}\n\t},\n\n\t/**\n\t * Recursively deep copies `source` properties into `target` with the given `options`.\n\t * IMPORTANT: `target` is not cloned and will be updated with `source` properties.\n\t * @param {Object} target - The target object in which all sources are merged into.\n\t * @param {Object|Array(Object)} source - Object(s) to merge into `target`.\n\t * @param {Object} [options] - Merging options:\n\t * @param {Function} [options.merger] - The merge method (key, target, source, options)\n\t * @returns {Object} The `target` object.\n\t */\n\tmerge: function(target, source, options) {\n\t\tvar sources = helpers.isArray(source) ? source : [source];\n\t\tvar ilen = sources.length;\n\t\tvar merge, i, keys, klen, k;\n\n\t\tif (!helpers.isObject(target)) {\n\t\t\treturn target;\n\t\t}\n\n\t\toptions = options || {};\n\t\tmerge = options.merger || helpers._merger;\n\n\t\tfor (i = 0; i < ilen; ++i) {\n\t\t\tsource = sources[i];\n\t\t\tif (!helpers.isObject(source)) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tkeys = Object.keys(source);\n\t\t\tfor (k = 0, klen = keys.length; k < klen; ++k) {\n\t\t\t\tmerge(keys[k], target, source, options);\n\t\t\t}\n\t\t}\n\n\t\treturn target;\n\t},\n\n\t/**\n\t * Recursively deep copies `source` properties into `target` *only* if not defined in target.\n\t * IMPORTANT: `target` is not cloned and will be updated with `source` properties.\n\t * @param {Object} target - The target object in which all sources are merged into.\n\t * @param {Object|Array(Object)} source - Object(s) to merge into `target`.\n\t * @returns {Object} The `target` object.\n\t */\n\tmergeIf: function(target, source) {\n\t\treturn helpers.merge(target, source, {merger: helpers._mergerIf});\n\t},\n\n\t/**\n\t * Applies the contents of two or more objects together into the first object.\n\t * @param {Object} target - The target object in which all objects are merged into.\n\t * @param {Object} arg1 - Object containing additional properties to merge in target.\n\t * @param {Object} argN - Additional objects containing properties to merge in target.\n\t * @returns {Object} The `target` object.\n\t */\n\textend: function(target) {\n\t\tvar setFn = function(value, key) {\n\t\t\ttarget[key] = value;\n\t\t};\n\t\tfor (var i = 1, ilen = arguments.length; i < ilen; ++i) {\n\t\t\thelpers.each(arguments[i], setFn);\n\t\t}\n\t\treturn target;\n\t},\n\n\t/**\n\t * Basic javascript inheritance based on the model created in Backbone.js\n\t */\n\tinherits: function(extensions) {\n\t\tvar me = this;\n\t\tvar ChartElement = (extensions && extensions.hasOwnProperty('constructor')) ? extensions.constructor : function() {\n\t\t\treturn me.apply(this, arguments);\n\t\t};\n\n\t\tvar Surrogate = function() {\n\t\t\tthis.constructor = ChartElement;\n\t\t};\n\n\t\tSurrogate.prototype = me.prototype;\n\t\tChartElement.prototype = new Surrogate();\n\t\tChartElement.extend = helpers.inherits;\n\n\t\tif (extensions) {\n\t\t\thelpers.extend(ChartElement.prototype, extensions);\n\t\t}\n\n\t\tChartElement.__super__ = me.prototype;\n\t\treturn ChartElement;\n\t}\n};\n\nmodule.exports = helpers;\n\n// DEPRECATIONS\n\n/**\n * Provided for backward compatibility, use Chart.helpers.callback instead.\n * @function Chart.helpers.callCallback\n * @deprecated since version 2.6.0\n * @todo remove at version 3\n * @private\n */\nhelpers.callCallback = helpers.callback;\n\n/**\n * Provided for backward compatibility, use Array.prototype.indexOf instead.\n * Array.prototype.indexOf compatibility: Chrome, Opera, Safari, FF1.5+, IE9+\n * @function Chart.helpers.indexOf\n * @deprecated since version 2.7.0\n * @todo remove at version 3\n * @private\n */\nhelpers.indexOf = function(array, item, fromIndex) {\n\treturn Array.prototype.indexOf.call(array, item, fromIndex);\n};\n\n/**\n * Provided for backward compatibility, use Chart.helpers.valueOrDefault instead.\n * @function Chart.helpers.getValueOrDefault\n * @deprecated since version 2.7.0\n * @todo remove at version 3\n * @private\n */\nhelpers.getValueOrDefault = helpers.valueOrDefault;\n\n/**\n * Provided for backward compatibility, use Chart.helpers.valueAtIndexOrDefault instead.\n * @function Chart.helpers.getValueAtIndexOrDefault\n * @deprecated since version 2.7.0\n * @todo remove at version 3\n * @private\n */\nhelpers.getValueAtIndexOrDefault = helpers.valueAtIndexOrDefault;\n\n},{}],43:[function(require,module,exports){\n'use strict';\n\nvar helpers = require(42);\n\n/**\n * Easing functions adapted from Robert Penner's easing equations.\n * @namespace Chart.helpers.easingEffects\n * @see http://www.robertpenner.com/easing/\n */\nvar effects = {\n\tlinear: function(t) {\n\t\treturn t;\n\t},\n\n\teaseInQuad: function(t) {\n\t\treturn t * t;\n\t},\n\n\teaseOutQuad: function(t) {\n\t\treturn -t * (t - 2);\n\t},\n\n\teaseInOutQuad: function(t) {\n\t\tif ((t /= 0.5) < 1) {\n\t\t\treturn 0.5 * t * t;\n\t\t}\n\t\treturn -0.5 * ((--t) * (t - 2) - 1);\n\t},\n\n\teaseInCubic: function(t) {\n\t\treturn t * t * t;\n\t},\n\n\teaseOutCubic: function(t) {\n\t\treturn (t = t - 1) * t * t + 1;\n\t},\n\n\teaseInOutCubic: function(t) {\n\t\tif ((t /= 0.5) < 1) {\n\t\t\treturn 0.5 * t * t * t;\n\t\t}\n\t\treturn 0.5 * ((t -= 2) * t * t + 2);\n\t},\n\n\teaseInQuart: function(t) {\n\t\treturn t * t * t * t;\n\t},\n\n\teaseOutQuart: function(t) {\n\t\treturn -((t = t - 1) * t * t * t - 1);\n\t},\n\n\teaseInOutQuart: function(t) {\n\t\tif ((t /= 0.5) < 1) {\n\t\t\treturn 0.5 * t * t * t * t;\n\t\t}\n\t\treturn -0.5 * ((t -= 2) * t * t * t - 2);\n\t},\n\n\teaseInQuint: function(t) {\n\t\treturn t * t * t * t * t;\n\t},\n\n\teaseOutQuint: function(t) {\n\t\treturn (t = t - 1) * t * t * t * t + 1;\n\t},\n\n\teaseInOutQuint: function(t) {\n\t\tif ((t /= 0.5) < 1) {\n\t\t\treturn 0.5 * t * t * t * t * t;\n\t\t}\n\t\treturn 0.5 * ((t -= 2) * t * t * t * t + 2);\n\t},\n\n\teaseInSine: function(t) {\n\t\treturn -Math.cos(t * (Math.PI / 2)) + 1;\n\t},\n\n\teaseOutSine: function(t) {\n\t\treturn Math.sin(t * (Math.PI / 2));\n\t},\n\n\teaseInOutSine: function(t) {\n\t\treturn -0.5 * (Math.cos(Math.PI * t) - 1);\n\t},\n\n\teaseInExpo: function(t) {\n\t\treturn (t === 0) ? 0 : Math.pow(2, 10 * (t - 1));\n\t},\n\n\teaseOutExpo: function(t) {\n\t\treturn (t === 1) ? 1 : -Math.pow(2, -10 * t) + 1;\n\t},\n\n\teaseInOutExpo: function(t) {\n\t\tif (t === 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (t === 1) {\n\t\t\treturn 1;\n\t\t}\n\t\tif ((t /= 0.5) < 1) {\n\t\t\treturn 0.5 * Math.pow(2, 10 * (t - 1));\n\t\t}\n\t\treturn 0.5 * (-Math.pow(2, -10 * --t) + 2);\n\t},\n\n\teaseInCirc: function(t) {\n\t\tif (t >= 1) {\n\t\t\treturn t;\n\t\t}\n\t\treturn -(Math.sqrt(1 - t * t) - 1);\n\t},\n\n\teaseOutCirc: function(t) {\n\t\treturn Math.sqrt(1 - (t = t - 1) * t);\n\t},\n\n\teaseInOutCirc: function(t) {\n\t\tif ((t /= 0.5) < 1) {\n\t\t\treturn -0.5 * (Math.sqrt(1 - t * t) - 1);\n\t\t}\n\t\treturn 0.5 * (Math.sqrt(1 - (t -= 2) * t) + 1);\n\t},\n\n\teaseInElastic: function(t) {\n\t\tvar s = 1.70158;\n\t\tvar p = 0;\n\t\tvar a = 1;\n\t\tif (t === 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (t === 1) {\n\t\t\treturn 1;\n\t\t}\n\t\tif (!p) {\n\t\t\tp = 0.3;\n\t\t}\n\t\tif (a < 1) {\n\t\t\ta = 1;\n\t\t\ts = p / 4;\n\t\t} else {\n\t\t\ts = p / (2 * Math.PI) * Math.asin(1 / a);\n\t\t}\n\t\treturn -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));\n\t},\n\n\teaseOutElastic: function(t) {\n\t\tvar s = 1.70158;\n\t\tvar p = 0;\n\t\tvar a = 1;\n\t\tif (t === 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tif (t === 1) {\n\t\t\treturn 1;\n\t\t}\n\t\tif (!p) {\n\t\t\tp = 0.3;\n\t\t}\n\t\tif (a < 1) {\n\t\t\ta = 1;\n\t\t\ts = p / 4;\n\t\t} else {\n\t\t\ts = p / (2 * Math.PI) * Math.asin(1 / a);\n\t\t}\n\t\treturn a * Math.pow(2, -10 * t) * Math.sin((t - s) * (2 * Math.PI) / p) + 1;\n\t},\n\n\teaseInOutElastic: function(t) {\n\t\tvar s = 1.70158;\n\t\tvar p = 0;\n\t\tvar a = 1;\n\t\tif (t === 0) {\n\t\t\treturn 0;\n\t\t}\n\t\tif ((t /= 0.5) === 2) {\n\t\t\treturn 1;\n\t\t}\n\t\tif (!p) {\n\t\t\tp = 0.45;\n\t\t}\n\t\tif (a < 1) {\n\t\t\ta = 1;\n\t\t\ts = p / 4;\n\t\t} else {\n\t\t\ts = p / (2 * Math.PI) * Math.asin(1 / a);\n\t\t}\n\t\tif (t < 1) {\n\t\t\treturn -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p));\n\t\t}\n\t\treturn a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t - s) * (2 * Math.PI) / p) * 0.5 + 1;\n\t},\n\teaseInBack: function(t) {\n\t\tvar s = 1.70158;\n\t\treturn t * t * ((s + 1) * t - s);\n\t},\n\n\teaseOutBack: function(t) {\n\t\tvar s = 1.70158;\n\t\treturn (t = t - 1) * t * ((s + 1) * t + s) + 1;\n\t},\n\n\teaseInOutBack: function(t) {\n\t\tvar s = 1.70158;\n\t\tif ((t /= 0.5) < 1) {\n\t\t\treturn 0.5 * (t * t * (((s *= (1.525)) + 1) * t - s));\n\t\t}\n\t\treturn 0.5 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);\n\t},\n\n\teaseInBounce: function(t) {\n\t\treturn 1 - effects.easeOutBounce(1 - t);\n\t},\n\n\teaseOutBounce: function(t) {\n\t\tif (t < (1 / 2.75)) {\n\t\t\treturn 7.5625 * t * t;\n\t\t}\n\t\tif (t < (2 / 2.75)) {\n\t\t\treturn 7.5625 * (t -= (1.5 / 2.75)) * t + 0.75;\n\t\t}\n\t\tif (t < (2.5 / 2.75)) {\n\t\t\treturn 7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375;\n\t\t}\n\t\treturn 7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375;\n\t},\n\n\teaseInOutBounce: function(t) {\n\t\tif (t < 0.5) {\n\t\t\treturn effects.easeInBounce(t * 2) * 0.5;\n\t\t}\n\t\treturn effects.easeOutBounce(t * 2 - 1) * 0.5 + 0.5;\n\t}\n};\n\nmodule.exports = {\n\teffects: effects\n};\n\n// DEPRECATIONS\n\n/**\n * Provided for backward compatibility, use Chart.helpers.easing.effects instead.\n * @function Chart.helpers.easingEffects\n * @deprecated since version 2.7.0\n * @todo remove at version 3\n * @private\n */\nhelpers.easingEffects = effects;\n\n},{\"42\":42}],44:[function(require,module,exports){\n'use strict';\n\nvar helpers = require(42);\n\n/**\n * @alias Chart.helpers.options\n * @namespace\n */\nmodule.exports = {\n\t/**\n\t * Converts the given line height `value` in pixels for a specific font `size`.\n\t * @param {Number|String} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').\n\t * @param {Number} size - The font size (in pixels) used to resolve relative `value`.\n\t * @returns {Number} The effective line height in pixels (size * 1.2 if value is invalid).\n\t * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height\n\t * @since 2.7.0\n\t */\n\ttoLineHeight: function(value, size) {\n\t\tvar matches = ('' + value).match(/^(normal|(\\d+(?:\\.\\d+)?)(px|em|%)?)$/);\n\t\tif (!matches || matches[1] === 'normal') {\n\t\t\treturn size * 1.2;\n\t\t}\n\n\t\tvalue = +matches[2];\n\n\t\tswitch (matches[3]) {\n\t\tcase 'px':\n\t\t\treturn value;\n\t\tcase '%':\n\t\t\tvalue /= 100;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\n\t\treturn size * value;\n\t},\n\n\t/**\n\t * Converts the given value into a padding object with pre-computed width/height.\n\t * @param {Number|Object} value - If a number, set the value to all TRBL component,\n\t *  else, if and object, use defined properties and sets undefined ones to 0.\n\t * @returns {Object} The padding values (top, right, bottom, left, width, height)\n\t * @since 2.7.0\n\t */\n\ttoPadding: function(value) {\n\t\tvar t, r, b, l;\n\n\t\tif (helpers.isObject(value)) {\n\t\t\tt = +value.top || 0;\n\t\t\tr = +value.right || 0;\n\t\t\tb = +value.bottom || 0;\n\t\t\tl = +value.left || 0;\n\t\t} else {\n\t\t\tt = r = b = l = +value || 0;\n\t\t}\n\n\t\treturn {\n\t\t\ttop: t,\n\t\t\tright: r,\n\t\t\tbottom: b,\n\t\t\tleft: l,\n\t\t\theight: t + b,\n\t\t\twidth: l + r\n\t\t};\n\t},\n\n\t/**\n\t * Evaluates the given `inputs` sequentially and returns the first defined value.\n\t * @param {Array[]} inputs - An array of values, falling back to the last value.\n\t * @param {Object} [context] - If defined and the current value is a function, the value\n\t * is called with `context` as first argument and the result becomes the new input.\n\t * @param {Number} [index] - If defined and the current value is an array, the value\n\t * at `index` become the new input.\n\t * @since 2.7.0\n\t */\n\tresolve: function(inputs, context, index) {\n\t\tvar i, ilen, value;\n\n\t\tfor (i = 0, ilen = inputs.length; i < ilen; ++i) {\n\t\t\tvalue = inputs[i];\n\t\t\tif (value === undefined) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (context !== undefined && typeof value === 'function') {\n\t\t\t\tvalue = value(context);\n\t\t\t}\n\t\t\tif (index !== undefined && helpers.isArray(value)) {\n\t\t\t\tvalue = value[index];\n\t\t\t}\n\t\t\tif (value !== undefined) {\n\t\t\t\treturn value;\n\t\t\t}\n\t\t}\n\t}\n};\n\n},{\"42\":42}],45:[function(require,module,exports){\n'use strict';\n\nmodule.exports = require(42);\nmodule.exports.easing = require(43);\nmodule.exports.canvas = require(41);\nmodule.exports.options = require(44);\n\n},{\"41\":41,\"42\":42,\"43\":43,\"44\":44}],46:[function(require,module,exports){\n/**\n * Platform fallback implementation (minimal).\n * @see https://github.com/chartjs/Chart.js/pull/4591#issuecomment-319575939\n */\n\nmodule.exports = {\n\tacquireContext: function(item) {\n\t\tif (item && item.canvas) {\n\t\t\t// Support for any object associated to a canvas (including a context2d)\n\t\t\titem = item.canvas;\n\t\t}\n\n\t\treturn item && item.getContext('2d') || null;\n\t}\n};\n\n},{}],47:[function(require,module,exports){\n/**\n * Chart.Platform implementation for targeting a web browser\n */\n\n'use strict';\n\nvar helpers = require(45);\n\nvar EXPANDO_KEY = '$chartjs';\nvar CSS_PREFIX = 'chartjs-';\nvar CSS_RENDER_MONITOR = CSS_PREFIX + 'render-monitor';\nvar CSS_RENDER_ANIMATION = CSS_PREFIX + 'render-animation';\nvar ANIMATION_START_EVENTS = ['animationstart', 'webkitAnimationStart'];\n\n/**\n * DOM event types -> Chart.js event types.\n * Note: only events with different types are mapped.\n * @see https://developer.mozilla.org/en-US/docs/Web/Events\n */\nvar EVENT_TYPES = {\n\ttouchstart: 'mousedown',\n\ttouchmove: 'mousemove',\n\ttouchend: 'mouseup',\n\tpointerenter: 'mouseenter',\n\tpointerdown: 'mousedown',\n\tpointermove: 'mousemove',\n\tpointerup: 'mouseup',\n\tpointerleave: 'mouseout',\n\tpointerout: 'mouseout'\n};\n\n/**\n * The \"used\" size is the final value of a dimension property after all calculations have\n * been performed. This method uses the computed style of `element` but returns undefined\n * if the computed style is not expressed in pixels. That can happen in some cases where\n * `element` has a size relative to its parent and this last one is not yet displayed,\n * for example because of `display: none` on a parent node.\n * @see https://developer.mozilla.org/en-US/docs/Web/CSS/used_value\n * @returns {Number} Size in pixels or undefined if unknown.\n */\nfunction readUsedSize(element, property) {\n\tvar value = helpers.getStyle(element, property);\n\tvar matches = value && value.match(/^(\\d+)(\\.\\d+)?px$/);\n\treturn matches ? Number(matches[1]) : undefined;\n}\n\n/**\n * Initializes the canvas style and render size without modifying the canvas display size,\n * since responsiveness is handled by the controller.resize() method. The config is used\n * to determine the aspect ratio to apply in case no explicit height has been specified.\n */\nfunction initCanvas(canvas, config) {\n\tvar style = canvas.style;\n\n\t// NOTE(SB) canvas.getAttribute('width') !== canvas.width: in the first case it\n\t// returns null or '' if no explicit value has been set to the canvas attribute.\n\tvar renderHeight = canvas.getAttribute('height');\n\tvar renderWidth = canvas.getAttribute('width');\n\n\t// Chart.js modifies some canvas values that we want to restore on destroy\n\tcanvas[EXPANDO_KEY] = {\n\t\tinitial: {\n\t\t\theight: renderHeight,\n\t\t\twidth: renderWidth,\n\t\t\tstyle: {\n\t\t\t\tdisplay: style.display,\n\t\t\t\theight: style.height,\n\t\t\t\twidth: style.width\n\t\t\t}\n\t\t}\n\t};\n\n\t// Force canvas to display as block to avoid extra space caused by inline\n\t// elements, which would interfere with the responsive resize process.\n\t// https://github.com/chartjs/Chart.js/issues/2538\n\tstyle.display = style.display || 'block';\n\n\tif (renderWidth === null || renderWidth === '') {\n\t\tvar displayWidth = readUsedSize(canvas, 'width');\n\t\tif (displayWidth !== undefined) {\n\t\t\tcanvas.width = displayWidth;\n\t\t}\n\t}\n\n\tif (renderHeight === null || renderHeight === '') {\n\t\tif (canvas.style.height === '') {\n\t\t\t// If no explicit render height and style height, let's apply the aspect ratio,\n\t\t\t// which one can be specified by the user but also by charts as default option\n\t\t\t// (i.e. options.aspectRatio). If not specified, use canvas aspect ratio of 2.\n\t\t\tcanvas.height = canvas.width / (config.options.aspectRatio || 2);\n\t\t} else {\n\t\t\tvar displayHeight = readUsedSize(canvas, 'height');\n\t\t\tif (displayWidth !== undefined) {\n\t\t\t\tcanvas.height = displayHeight;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn canvas;\n}\n\n/**\n * Detects support for options object argument in addEventListener.\n * https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support\n * @private\n */\nvar supportsEventListenerOptions = (function() {\n\tvar supports = false;\n\ttry {\n\t\tvar options = Object.defineProperty({}, 'passive', {\n\t\t\tget: function() {\n\t\t\t\tsupports = true;\n\t\t\t}\n\t\t});\n\t\twindow.addEventListener('e', null, options);\n\t} catch (e) {\n\t\t// continue regardless of error\n\t}\n\treturn supports;\n}());\n\n// Default passive to true as expected by Chrome for 'touchstart' and 'touchend' events.\n// https://github.com/chartjs/Chart.js/issues/4287\nvar eventListenerOptions = supportsEventListenerOptions ? {passive: true} : false;\n\nfunction addEventListener(node, type, listener) {\n\tnode.addEventListener(type, listener, eventListenerOptions);\n}\n\nfunction removeEventListener(node, type, listener) {\n\tnode.removeEventListener(type, listener, eventListenerOptions);\n}\n\nfunction createEvent(type, chart, x, y, nativeEvent) {\n\treturn {\n\t\ttype: type,\n\t\tchart: chart,\n\t\tnative: nativeEvent || null,\n\t\tx: x !== undefined ? x : null,\n\t\ty: y !== undefined ? y : null,\n\t};\n}\n\nfunction fromNativeEvent(event, chart) {\n\tvar type = EVENT_TYPES[event.type] || event.type;\n\tvar pos = helpers.getRelativePosition(event, chart);\n\treturn createEvent(type, chart, pos.x, pos.y, event);\n}\n\nfunction throttled(fn, thisArg) {\n\tvar ticking = false;\n\tvar args = [];\n\n\treturn function() {\n\t\targs = Array.prototype.slice.call(arguments);\n\t\tthisArg = thisArg || this;\n\n\t\tif (!ticking) {\n\t\t\tticking = true;\n\t\t\thelpers.requestAnimFrame.call(window, function() {\n\t\t\t\tticking = false;\n\t\t\t\tfn.apply(thisArg, args);\n\t\t\t});\n\t\t}\n\t};\n}\n\n// Implementation based on https://github.com/marcj/css-element-queries\nfunction createResizer(handler) {\n\tvar resizer = document.createElement('div');\n\tvar cls = CSS_PREFIX + 'size-monitor';\n\tvar maxSize = 1000000;\n\tvar style =\n\t\t'position:absolute;' +\n\t\t'left:0;' +\n\t\t'top:0;' +\n\t\t'right:0;' +\n\t\t'bottom:0;' +\n\t\t'overflow:hidden;' +\n\t\t'pointer-events:none;' +\n\t\t'visibility:hidden;' +\n\t\t'z-index:-1;';\n\n\tresizer.style.cssText = style;\n\tresizer.className = cls;\n\tresizer.innerHTML =\n\t\t'<div class=\"' + cls + '-expand\" style=\"' + style + '\">' +\n\t\t\t'<div style=\"' +\n\t\t\t\t'position:absolute;' +\n\t\t\t\t'width:' + maxSize + 'px;' +\n\t\t\t\t'height:' + maxSize + 'px;' +\n\t\t\t\t'left:0;' +\n\t\t\t\t'top:0\">' +\n\t\t\t'</div>' +\n\t\t'</div>' +\n\t\t'<div class=\"' + cls + '-shrink\" style=\"' + style + '\">' +\n\t\t\t'<div style=\"' +\n\t\t\t\t'position:absolute;' +\n\t\t\t\t'width:200%;' +\n\t\t\t\t'height:200%;' +\n\t\t\t\t'left:0; ' +\n\t\t\t\t'top:0\">' +\n\t\t\t'</div>' +\n\t\t'</div>';\n\n\tvar expand = resizer.childNodes[0];\n\tvar shrink = resizer.childNodes[1];\n\n\tresizer._reset = function() {\n\t\texpand.scrollLeft = maxSize;\n\t\texpand.scrollTop = maxSize;\n\t\tshrink.scrollLeft = maxSize;\n\t\tshrink.scrollTop = maxSize;\n\t};\n\tvar onScroll = function() {\n\t\tresizer._reset();\n\t\thandler();\n\t};\n\n\taddEventListener(expand, 'scroll', onScroll.bind(expand, 'expand'));\n\taddEventListener(shrink, 'scroll', onScroll.bind(shrink, 'shrink'));\n\n\treturn resizer;\n}\n\n// https://davidwalsh.name/detect-node-insertion\nfunction watchForRender(node, handler) {\n\tvar expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});\n\tvar proxy = expando.renderProxy = function(e) {\n\t\tif (e.animationName === CSS_RENDER_ANIMATION) {\n\t\t\thandler();\n\t\t}\n\t};\n\n\thelpers.each(ANIMATION_START_EVENTS, function(type) {\n\t\taddEventListener(node, type, proxy);\n\t});\n\n\t// #4737: Chrome might skip the CSS animation when the CSS_RENDER_MONITOR class\n\t// is removed then added back immediately (same animation frame?). Accessing the\n\t// `offsetParent` property will force a reflow and re-evaluate the CSS animation.\n\t// https://gist.github.com/paulirish/5d52fb081b3570c81e3a#box-metrics\n\t// https://github.com/chartjs/Chart.js/issues/4737\n\texpando.reflow = !!node.offsetParent;\n\n\tnode.classList.add(CSS_RENDER_MONITOR);\n}\n\nfunction unwatchForRender(node) {\n\tvar expando = node[EXPANDO_KEY] || {};\n\tvar proxy = expando.renderProxy;\n\n\tif (proxy) {\n\t\thelpers.each(ANIMATION_START_EVENTS, function(type) {\n\t\t\tremoveEventListener(node, type, proxy);\n\t\t});\n\n\t\tdelete expando.renderProxy;\n\t}\n\n\tnode.classList.remove(CSS_RENDER_MONITOR);\n}\n\nfunction addResizeListener(node, listener, chart) {\n\tvar expando = node[EXPANDO_KEY] || (node[EXPANDO_KEY] = {});\n\n\t// Let's keep track of this added resizer and thus avoid DOM query when removing it.\n\tvar resizer = expando.resizer = createResizer(throttled(function() {\n\t\tif (expando.resizer) {\n\t\t\treturn listener(createEvent('resize', chart));\n\t\t}\n\t}));\n\n\t// The resizer needs to be attached to the node parent, so we first need to be\n\t// sure that `node` is attached to the DOM before injecting the resizer element.\n\twatchForRender(node, function() {\n\t\tif (expando.resizer) {\n\t\t\tvar container = node.parentNode;\n\t\t\tif (container && container !== resizer.parentNode) {\n\t\t\t\tcontainer.insertBefore(resizer, container.firstChild);\n\t\t\t}\n\n\t\t\t// The container size might have changed, let's reset the resizer state.\n\t\t\tresizer._reset();\n\t\t}\n\t});\n}\n\nfunction removeResizeListener(node) {\n\tvar expando = node[EXPANDO_KEY] || {};\n\tvar resizer = expando.resizer;\n\n\tdelete expando.resizer;\n\tunwatchForRender(node);\n\n\tif (resizer && resizer.parentNode) {\n\t\tresizer.parentNode.removeChild(resizer);\n\t}\n}\n\nfunction injectCSS(platform, css) {\n\t// http://stackoverflow.com/q/3922139\n\tvar style = platform._style || document.createElement('style');\n\tif (!platform._style) {\n\t\tplatform._style = style;\n\t\tcss = '/* Chart.js */\\n' + css;\n\t\tstyle.setAttribute('type', 'text/css');\n\t\tdocument.getElementsByTagName('head')[0].appendChild(style);\n\t}\n\n\tstyle.appendChild(document.createTextNode(css));\n}\n\nmodule.exports = {\n\t/**\n\t * This property holds whether this platform is enabled for the current environment.\n\t * Currently used by platform.js to select the proper implementation.\n\t * @private\n\t */\n\t_enabled: typeof window !== 'undefined' && typeof document !== 'undefined',\n\n\tinitialize: function() {\n\t\tvar keyframes = 'from{opacity:0.99}to{opacity:1}';\n\n\t\tinjectCSS(this,\n\t\t\t// DOM rendering detection\n\t\t\t// https://davidwalsh.name/detect-node-insertion\n\t\t\t'@-webkit-keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' +\n\t\t\t'@keyframes ' + CSS_RENDER_ANIMATION + '{' + keyframes + '}' +\n\t\t\t'.' + CSS_RENDER_MONITOR + '{' +\n\t\t\t\t'-webkit-animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' +\n\t\t\t\t'animation:' + CSS_RENDER_ANIMATION + ' 0.001s;' +\n\t\t\t'}'\n\t\t);\n\t},\n\n\tacquireContext: function(item, config) {\n\t\tif (typeof item === 'string') {\n\t\t\titem = document.getElementById(item);\n\t\t} else if (item.length) {\n\t\t\t// Support for array based queries (such as jQuery)\n\t\t\titem = item[0];\n\t\t}\n\n\t\tif (item && item.canvas) {\n\t\t\t// Support for any object associated to a canvas (including a context2d)\n\t\t\titem = item.canvas;\n\t\t}\n\n\t\t// To prevent canvas fingerprinting, some add-ons undefine the getContext\n\t\t// method, for example: https://github.com/kkapsner/CanvasBlocker\n\t\t// https://github.com/chartjs/Chart.js/issues/2807\n\t\tvar context = item && item.getContext && item.getContext('2d');\n\n\t\t// `instanceof HTMLCanvasElement/CanvasRenderingContext2D` fails when the item is\n\t\t// inside an iframe or when running in a protected environment. We could guess the\n\t\t// types from their toString() value but let's keep things flexible and assume it's\n\t\t// a sufficient condition if the item has a context2D which has item as `canvas`.\n\t\t// https://github.com/chartjs/Chart.js/issues/3887\n\t\t// https://github.com/chartjs/Chart.js/issues/4102\n\t\t// https://github.com/chartjs/Chart.js/issues/4152\n\t\tif (context && context.canvas === item) {\n\t\t\tinitCanvas(item, config);\n\t\t\treturn context;\n\t\t}\n\n\t\treturn null;\n\t},\n\n\treleaseContext: function(context) {\n\t\tvar canvas = context.canvas;\n\t\tif (!canvas[EXPANDO_KEY]) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar initial = canvas[EXPANDO_KEY].initial;\n\t\t['height', 'width'].forEach(function(prop) {\n\t\t\tvar value = initial[prop];\n\t\t\tif (helpers.isNullOrUndef(value)) {\n\t\t\t\tcanvas.removeAttribute(prop);\n\t\t\t} else {\n\t\t\t\tcanvas.setAttribute(prop, value);\n\t\t\t}\n\t\t});\n\n\t\thelpers.each(initial.style || {}, function(value, key) {\n\t\t\tcanvas.style[key] = value;\n\t\t});\n\n\t\t// The canvas render size might have been changed (and thus the state stack discarded),\n\t\t// we can't use save() and restore() to restore the initial state. So make sure that at\n\t\t// least the canvas context is reset to the default state by setting the canvas width.\n\t\t// https://www.w3.org/TR/2011/WD-html5-20110525/the-canvas-element.html\n\t\tcanvas.width = canvas.width;\n\n\t\tdelete canvas[EXPANDO_KEY];\n\t},\n\n\taddEventListener: function(chart, type, listener) {\n\t\tvar canvas = chart.canvas;\n\t\tif (type === 'resize') {\n\t\t\t// Note: the resize event is not supported on all browsers.\n\t\t\taddResizeListener(canvas, listener, chart);\n\t\t\treturn;\n\t\t}\n\n\t\tvar expando = listener[EXPANDO_KEY] || (listener[EXPANDO_KEY] = {});\n\t\tvar proxies = expando.proxies || (expando.proxies = {});\n\t\tvar proxy = proxies[chart.id + '_' + type] = function(event) {\n\t\t\tlistener(fromNativeEvent(event, chart));\n\t\t};\n\n\t\taddEventListener(canvas, type, proxy);\n\t},\n\n\tremoveEventListener: function(chart, type, listener) {\n\t\tvar canvas = chart.canvas;\n\t\tif (type === 'resize') {\n\t\t\t// Note: the resize event is not supported on all browsers.\n\t\t\tremoveResizeListener(canvas, listener);\n\t\t\treturn;\n\t\t}\n\n\t\tvar expando = listener[EXPANDO_KEY] || {};\n\t\tvar proxies = expando.proxies || {};\n\t\tvar proxy = proxies[chart.id + '_' + type];\n\t\tif (!proxy) {\n\t\t\treturn;\n\t\t}\n\n\t\tremoveEventListener(canvas, type, proxy);\n\t}\n};\n\n// DEPRECATIONS\n\n/**\n * Provided for backward compatibility, use EventTarget.addEventListener instead.\n * EventTarget.addEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+\n * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener\n * @function Chart.helpers.addEvent\n * @deprecated since version 2.7.0\n * @todo remove at version 3\n * @private\n */\nhelpers.addEvent = addEventListener;\n\n/**\n * Provided for backward compatibility, use EventTarget.removeEventListener instead.\n * EventTarget.removeEventListener compatibility: Chrome, Opera 7, Safari, FF1.5+, IE9+\n * @see https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/removeEventListener\n * @function Chart.helpers.removeEvent\n * @deprecated since version 2.7.0\n * @todo remove at version 3\n * @private\n */\nhelpers.removeEvent = removeEventListener;\n\n},{\"45\":45}],48:[function(require,module,exports){\n'use strict';\n\nvar helpers = require(45);\nvar basic = require(46);\nvar dom = require(47);\n\n// @TODO Make possible to select another platform at build time.\nvar implementation = dom._enabled ? dom : basic;\n\n/**\n * @namespace Chart.platform\n * @see https://chartjs.gitbooks.io/proposals/content/Platform.html\n * @since 2.4.0\n */\nmodule.exports = helpers.extend({\n\t/**\n\t * @since 2.7.0\n\t */\n\tinitialize: function() {},\n\n\t/**\n\t * Called at chart construction time, returns a context2d instance implementing\n\t * the [W3C Canvas 2D Context API standard]{@link https://www.w3.org/TR/2dcontext/}.\n\t * @param {*} item - The native item from which to acquire context (platform specific)\n\t * @param {Object} options - The chart options\n\t * @returns {CanvasRenderingContext2D} context2d instance\n\t */\n\tacquireContext: function() {},\n\n\t/**\n\t * Called at chart destruction time, releases any resources associated to the context\n\t * previously returned by the acquireContext() method.\n\t * @param {CanvasRenderingContext2D} context - The context2d instance\n\t * @returns {Boolean} true if the method succeeded, else false\n\t */\n\treleaseContext: function() {},\n\n\t/**\n\t * Registers the specified listener on the given chart.\n\t * @param {Chart} chart - Chart from which to listen for event\n\t * @param {String} type - The ({@link IEvent}) type to listen for\n\t * @param {Function} listener - Receives a notification (an object that implements\n\t * the {@link IEvent} interface) when an event of the specified type occurs.\n\t */\n\taddEventListener: function() {},\n\n\t/**\n\t * Removes the specified listener previously registered with addEventListener.\n\t * @param {Chart} chart -Chart from which to remove the listener\n\t * @param {String} type - The ({@link IEvent}) type to remove\n\t * @param {Function} listener - The listener function to remove from the event target.\n\t */\n\tremoveEventListener: function() {}\n\n}, implementation);\n\n/**\n * @interface IPlatform\n * Allows abstracting platform dependencies away from the chart\n * @borrows Chart.platform.acquireContext as acquireContext\n * @borrows Chart.platform.releaseContext as releaseContext\n * @borrows Chart.platform.addEventListener as addEventListener\n * @borrows Chart.platform.removeEventListener as removeEventListener\n */\n\n/**\n * @interface IEvent\n * @prop {String} type - The event type name, possible values are:\n * 'contextmenu', 'mouseenter', 'mousedown', 'mousemove', 'mouseup', 'mouseout',\n * 'click', 'dblclick', 'keydown', 'keypress', 'keyup' and 'resize'\n * @prop {*} native - The original native event (null for emulated events, e.g. 'resize')\n * @prop {Number} x - The mouse x position, relative to the canvas (null for incompatible events)\n * @prop {Number} y - The mouse y position, relative to the canvas (null for incompatible events)\n */\n\n},{\"45\":45,\"46\":46,\"47\":47}],49:[function(require,module,exports){\n'use strict';\n\nmodule.exports = {};\nmodule.exports.filler = require(50);\nmodule.exports.legend = require(51);\nmodule.exports.title = require(52);\n\n},{\"50\":50,\"51\":51,\"52\":52}],50:[function(require,module,exports){\n/**\n * Plugin based on discussion from the following Chart.js issues:\n * @see https://github.com/chartjs/Chart.js/issues/2380#issuecomment-279961569\n * @see https://github.com/chartjs/Chart.js/issues/2440#issuecomment-256461897\n */\n\n'use strict';\n\nvar defaults = require(25);\nvar elements = require(40);\nvar helpers = require(45);\n\ndefaults._set('global', {\n\tplugins: {\n\t\tfiller: {\n\t\t\tpropagate: true\n\t\t}\n\t}\n});\n\nvar mappers = {\n\tdataset: function(source) {\n\t\tvar index = source.fill;\n\t\tvar chart = source.chart;\n\t\tvar meta = chart.getDatasetMeta(index);\n\t\tvar visible = meta && chart.isDatasetVisible(index);\n\t\tvar points = (visible && meta.dataset._children) || [];\n\t\tvar length = points.length || 0;\n\n\t\treturn !length ? null : function(point, i) {\n\t\t\treturn (i < length && points[i]._view) || null;\n\t\t};\n\t},\n\n\tboundary: function(source) {\n\t\tvar boundary = source.boundary;\n\t\tvar x = boundary ? boundary.x : null;\n\t\tvar y = boundary ? boundary.y : null;\n\n\t\treturn function(point) {\n\t\t\treturn {\n\t\t\t\tx: x === null ? point.x : x,\n\t\t\t\ty: y === null ? point.y : y,\n\t\t\t};\n\t\t};\n\t}\n};\n\n// @todo if (fill[0] === '#')\nfunction decodeFill(el, index, count) {\n\tvar model = el._model || {};\n\tvar fill = model.fill;\n\tvar target;\n\n\tif (fill === undefined) {\n\t\tfill = !!model.backgroundColor;\n\t}\n\n\tif (fill === false || fill === null) {\n\t\treturn false;\n\t}\n\n\tif (fill === true) {\n\t\treturn 'origin';\n\t}\n\n\ttarget = parseFloat(fill, 10);\n\tif (isFinite(target) && Math.floor(target) === target) {\n\t\tif (fill[0] === '-' || fill[0] === '+') {\n\t\t\ttarget = index + target;\n\t\t}\n\n\t\tif (target === index || target < 0 || target >= count) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn target;\n\t}\n\n\tswitch (fill) {\n\t// compatibility\n\tcase 'bottom':\n\t\treturn 'start';\n\tcase 'top':\n\t\treturn 'end';\n\tcase 'zero':\n\t\treturn 'origin';\n\t// supported boundaries\n\tcase 'origin':\n\tcase 'start':\n\tcase 'end':\n\t\treturn fill;\n\t// invalid fill values\n\tdefault:\n\t\treturn false;\n\t}\n}\n\nfunction computeBoundary(source) {\n\tvar model = source.el._model || {};\n\tvar scale = source.el._scale || {};\n\tvar fill = source.fill;\n\tvar target = null;\n\tvar horizontal;\n\n\tif (isFinite(fill)) {\n\t\treturn null;\n\t}\n\n\t// Backward compatibility: until v3, we still need to support boundary values set on\n\t// the model (scaleTop, scaleBottom and scaleZero) because some external plugins and\n\t// controllers might still use it (e.g. the Smith chart).\n\n\tif (fill === 'start') {\n\t\ttarget = model.scaleBottom === undefined ? scale.bottom : model.scaleBottom;\n\t} else if (fill === 'end') {\n\t\ttarget = model.scaleTop === undefined ? scale.top : model.scaleTop;\n\t} else if (model.scaleZero !== undefined) {\n\t\ttarget = model.scaleZero;\n\t} else if (scale.getBasePosition) {\n\t\ttarget = scale.getBasePosition();\n\t} else if (scale.getBasePixel) {\n\t\ttarget = scale.getBasePixel();\n\t}\n\n\tif (target !== undefined && target !== null) {\n\t\tif (target.x !== undefined && target.y !== undefined) {\n\t\t\treturn target;\n\t\t}\n\n\t\tif (typeof target === 'number' && isFinite(target)) {\n\t\t\thorizontal = scale.isHorizontal();\n\t\t\treturn {\n\t\t\t\tx: horizontal ? target : null,\n\t\t\t\ty: horizontal ? null : target\n\t\t\t};\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction resolveTarget(sources, index, propagate) {\n\tvar source = sources[index];\n\tvar fill = source.fill;\n\tvar visited = [index];\n\tvar target;\n\n\tif (!propagate) {\n\t\treturn fill;\n\t}\n\n\twhile (fill !== false && visited.indexOf(fill) === -1) {\n\t\tif (!isFinite(fill)) {\n\t\t\treturn fill;\n\t\t}\n\n\t\ttarget = sources[fill];\n\t\tif (!target) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (target.visible) {\n\t\t\treturn fill;\n\t\t}\n\n\t\tvisited.push(fill);\n\t\tfill = target.fill;\n\t}\n\n\treturn false;\n}\n\nfunction createMapper(source) {\n\tvar fill = source.fill;\n\tvar type = 'dataset';\n\n\tif (fill === false) {\n\t\treturn null;\n\t}\n\n\tif (!isFinite(fill)) {\n\t\ttype = 'boundary';\n\t}\n\n\treturn mappers[type](source);\n}\n\nfunction isDrawable(point) {\n\treturn point && !point.skip;\n}\n\nfunction drawArea(ctx, curve0, curve1, len0, len1) {\n\tvar i;\n\n\tif (!len0 || !len1) {\n\t\treturn;\n\t}\n\n\t// building first area curve (normal)\n\tctx.moveTo(curve0[0].x, curve0[0].y);\n\tfor (i = 1; i < len0; ++i) {\n\t\thelpers.canvas.lineTo(ctx, curve0[i - 1], curve0[i]);\n\t}\n\n\t// joining the two area curves\n\tctx.lineTo(curve1[len1 - 1].x, curve1[len1 - 1].y);\n\n\t// building opposite area curve (reverse)\n\tfor (i = len1 - 1; i > 0; --i) {\n\t\thelpers.canvas.lineTo(ctx, curve1[i], curve1[i - 1], true);\n\t}\n}\n\nfunction doFill(ctx, points, mapper, view, color, loop) {\n\tvar count = points.length;\n\tvar span = view.spanGaps;\n\tvar curve0 = [];\n\tvar curve1 = [];\n\tvar len0 = 0;\n\tvar len1 = 0;\n\tvar i, ilen, index, p0, p1, d0, d1;\n\n\tctx.beginPath();\n\n\tfor (i = 0, ilen = (count + !!loop); i < ilen; ++i) {\n\t\tindex = i % count;\n\t\tp0 = points[index]._view;\n\t\tp1 = mapper(p0, index, view);\n\t\td0 = isDrawable(p0);\n\t\td1 = isDrawable(p1);\n\n\t\tif (d0 && d1) {\n\t\t\tlen0 = curve0.push(p0);\n\t\t\tlen1 = curve1.push(p1);\n\t\t} else if (len0 && len1) {\n\t\t\tif (!span) {\n\t\t\t\tdrawArea(ctx, curve0, curve1, len0, len1);\n\t\t\t\tlen0 = len1 = 0;\n\t\t\t\tcurve0 = [];\n\t\t\t\tcurve1 = [];\n\t\t\t} else {\n\t\t\t\tif (d0) {\n\t\t\t\t\tcurve0.push(p0);\n\t\t\t\t}\n\t\t\t\tif (d1) {\n\t\t\t\t\tcurve1.push(p1);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tdrawArea(ctx, curve0, curve1, len0, len1);\n\n\tctx.closePath();\n\tctx.fillStyle = color;\n\tctx.fill();\n}\n\nmodule.exports = {\n\tid: 'filler',\n\n\tafterDatasetsUpdate: function(chart, options) {\n\t\tvar count = (chart.data.datasets || []).length;\n\t\tvar propagate = options.propagate;\n\t\tvar sources = [];\n\t\tvar meta, i, el, source;\n\n\t\tfor (i = 0; i < count; ++i) {\n\t\t\tmeta = chart.getDatasetMeta(i);\n\t\t\tel = meta.dataset;\n\t\t\tsource = null;\n\n\t\t\tif (el && el._model && el instanceof elements.Line) {\n\t\t\t\tsource = {\n\t\t\t\t\tvisible: chart.isDatasetVisible(i),\n\t\t\t\t\tfill: decodeFill(el, i, count),\n\t\t\t\t\tchart: chart,\n\t\t\t\t\tel: el\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tmeta.$filler = source;\n\t\t\tsources.push(source);\n\t\t}\n\n\t\tfor (i = 0; i < count; ++i) {\n\t\t\tsource = sources[i];\n\t\t\tif (!source) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tsource.fill = resolveTarget(sources, i, propagate);\n\t\t\tsource.boundary = computeBoundary(source);\n\t\t\tsource.mapper = createMapper(source);\n\t\t}\n\t},\n\n\tbeforeDatasetDraw: function(chart, args) {\n\t\tvar meta = args.meta.$filler;\n\t\tif (!meta) {\n\t\t\treturn;\n\t\t}\n\n\t\tvar ctx = chart.ctx;\n\t\tvar el = meta.el;\n\t\tvar view = el._view;\n\t\tvar points = el._children || [];\n\t\tvar mapper = meta.mapper;\n\t\tvar color = view.backgroundColor || defaults.global.defaultColor;\n\n\t\tif (mapper && color && points.length) {\n\t\t\thelpers.canvas.clipArea(ctx, chart.chartArea);\n\t\t\tdoFill(ctx, points, mapper, view, color, el._loop);\n\t\t\thelpers.canvas.unclipArea(ctx);\n\t\t}\n\t}\n};\n\n},{\"25\":25,\"40\":40,\"45\":45}],51:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\nvar Element = require(26);\nvar helpers = require(45);\nvar layouts = require(30);\n\nvar noop = helpers.noop;\n\ndefaults._set('global', {\n\tlegend: {\n\t\tdisplay: true,\n\t\tposition: 'top',\n\t\tfullWidth: true,\n\t\treverse: false,\n\t\tweight: 1000,\n\n\t\t// a callback that will handle\n\t\tonClick: function(e, legendItem) {\n\t\t\tvar index = legendItem.datasetIndex;\n\t\t\tvar ci = this.chart;\n\t\t\tvar meta = ci.getDatasetMeta(index);\n\n\t\t\t// See controller.isDatasetVisible comment\n\t\t\tmeta.hidden = meta.hidden === null ? !ci.data.datasets[index].hidden : null;\n\n\t\t\t// We hid a dataset ... rerender the chart\n\t\t\tci.update();\n\t\t},\n\n\t\tonHover: null,\n\n\t\tlabels: {\n\t\t\tboxWidth: 40,\n\t\t\tpadding: 10,\n\t\t\t// Generates labels shown in the legend\n\t\t\t// Valid properties to return:\n\t\t\t// text : text to display\n\t\t\t// fillStyle : fill of coloured box\n\t\t\t// strokeStyle: stroke of coloured box\n\t\t\t// hidden : if this legend item refers to a hidden item\n\t\t\t// lineCap : cap style for line\n\t\t\t// lineDash\n\t\t\t// lineDashOffset :\n\t\t\t// lineJoin :\n\t\t\t// lineWidth :\n\t\t\tgenerateLabels: function(chart) {\n\t\t\t\tvar data = chart.data;\n\t\t\t\treturn helpers.isArray(data.datasets) ? data.datasets.map(function(dataset, i) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttext: dataset.label,\n\t\t\t\t\t\tfillStyle: (!helpers.isArray(dataset.backgroundColor) ? dataset.backgroundColor : dataset.backgroundColor[0]),\n\t\t\t\t\t\thidden: !chart.isDatasetVisible(i),\n\t\t\t\t\t\tlineCap: dataset.borderCapStyle,\n\t\t\t\t\t\tlineDash: dataset.borderDash,\n\t\t\t\t\t\tlineDashOffset: dataset.borderDashOffset,\n\t\t\t\t\t\tlineJoin: dataset.borderJoinStyle,\n\t\t\t\t\t\tlineWidth: dataset.borderWidth,\n\t\t\t\t\t\tstrokeStyle: dataset.borderColor,\n\t\t\t\t\t\tpointStyle: dataset.pointStyle,\n\n\t\t\t\t\t\t// Below is extra data used for toggling the datasets\n\t\t\t\t\t\tdatasetIndex: i\n\t\t\t\t\t};\n\t\t\t\t}, this) : [];\n\t\t\t}\n\t\t}\n\t},\n\n\tlegendCallback: function(chart) {\n\t\tvar text = [];\n\t\ttext.push('<ul class=\"' + chart.id + '-legend\">');\n\t\tfor (var i = 0; i < chart.data.datasets.length; i++) {\n\t\t\ttext.push('<li><span style=\"background-color:' + chart.data.datasets[i].backgroundColor + '\"></span>');\n\t\t\tif (chart.data.datasets[i].label) {\n\t\t\t\ttext.push(chart.data.datasets[i].label);\n\t\t\t}\n\t\t\ttext.push('</li>');\n\t\t}\n\t\ttext.push('</ul>');\n\t\treturn text.join('');\n\t}\n});\n\n/**\n * Helper function to get the box width based on the usePointStyle option\n * @param labelopts {Object} the label options on the legend\n * @param fontSize {Number} the label font size\n * @return {Number} width of the color box area\n */\nfunction getBoxWidth(labelOpts, fontSize) {\n\treturn labelOpts.usePointStyle ?\n\t\tfontSize * Math.SQRT2 :\n\t\tlabelOpts.boxWidth;\n}\n\n/**\n * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required!\n */\nvar Legend = Element.extend({\n\n\tinitialize: function(config) {\n\t\thelpers.extend(this, config);\n\n\t\t// Contains hit boxes for each dataset (in dataset order)\n\t\tthis.legendHitBoxes = [];\n\n\t\t// Are we in doughnut mode which has a different data type\n\t\tthis.doughnutMode = false;\n\t},\n\n\t// These methods are ordered by lifecycle. Utilities then follow.\n\t// Any function defined here is inherited by all legend types.\n\t// Any function can be extended by the legend type\n\n\tbeforeUpdate: noop,\n\tupdate: function(maxWidth, maxHeight, margins) {\n\t\tvar me = this;\n\n\t\t// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)\n\t\tme.beforeUpdate();\n\n\t\t// Absorb the master measurements\n\t\tme.maxWidth = maxWidth;\n\t\tme.maxHeight = maxHeight;\n\t\tme.margins = margins;\n\n\t\t// Dimensions\n\t\tme.beforeSetDimensions();\n\t\tme.setDimensions();\n\t\tme.afterSetDimensions();\n\t\t// Labels\n\t\tme.beforeBuildLabels();\n\t\tme.buildLabels();\n\t\tme.afterBuildLabels();\n\n\t\t// Fit\n\t\tme.beforeFit();\n\t\tme.fit();\n\t\tme.afterFit();\n\t\t//\n\t\tme.afterUpdate();\n\n\t\treturn me.minSize;\n\t},\n\tafterUpdate: noop,\n\n\t//\n\n\tbeforeSetDimensions: noop,\n\tsetDimensions: function() {\n\t\tvar me = this;\n\t\t// Set the unconstrained dimension before label rotation\n\t\tif (me.isHorizontal()) {\n\t\t\t// Reset position before calculating rotation\n\t\t\tme.width = me.maxWidth;\n\t\t\tme.left = 0;\n\t\t\tme.right = me.width;\n\t\t} else {\n\t\t\tme.height = me.maxHeight;\n\n\t\t\t// Reset position before calculating rotation\n\t\t\tme.top = 0;\n\t\t\tme.bottom = me.height;\n\t\t}\n\n\t\t// Reset padding\n\t\tme.paddingLeft = 0;\n\t\tme.paddingTop = 0;\n\t\tme.paddingRight = 0;\n\t\tme.paddingBottom = 0;\n\n\t\t// Reset minSize\n\t\tme.minSize = {\n\t\t\twidth: 0,\n\t\t\theight: 0\n\t\t};\n\t},\n\tafterSetDimensions: noop,\n\n\t//\n\n\tbeforeBuildLabels: noop,\n\tbuildLabels: function() {\n\t\tvar me = this;\n\t\tvar labelOpts = me.options.labels || {};\n\t\tvar legendItems = helpers.callback(labelOpts.generateLabels, [me.chart], me) || [];\n\n\t\tif (labelOpts.filter) {\n\t\t\tlegendItems = legendItems.filter(function(item) {\n\t\t\t\treturn labelOpts.filter(item, me.chart.data);\n\t\t\t});\n\t\t}\n\n\t\tif (me.options.reverse) {\n\t\t\tlegendItems.reverse();\n\t\t}\n\n\t\tme.legendItems = legendItems;\n\t},\n\tafterBuildLabels: noop,\n\n\t//\n\n\tbeforeFit: noop,\n\tfit: function() {\n\t\tvar me = this;\n\t\tvar opts = me.options;\n\t\tvar labelOpts = opts.labels;\n\t\tvar display = opts.display;\n\n\t\tvar ctx = me.ctx;\n\n\t\tvar globalDefault = defaults.global;\n\t\tvar valueOrDefault = helpers.valueOrDefault;\n\t\tvar fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);\n\t\tvar fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);\n\t\tvar fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);\n\t\tvar labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);\n\n\t\t// Reset hit boxes\n\t\tvar hitboxes = me.legendHitBoxes = [];\n\n\t\tvar minSize = me.minSize;\n\t\tvar isHorizontal = me.isHorizontal();\n\n\t\tif (isHorizontal) {\n\t\t\tminSize.width = me.maxWidth; // fill all the width\n\t\t\tminSize.height = display ? 10 : 0;\n\t\t} else {\n\t\t\tminSize.width = display ? 10 : 0;\n\t\t\tminSize.height = me.maxHeight; // fill all the height\n\t\t}\n\n\t\t// Increase sizes here\n\t\tif (display) {\n\t\t\tctx.font = labelFont;\n\n\t\t\tif (isHorizontal) {\n\t\t\t\t// Labels\n\n\t\t\t\t// Width of each line of legend boxes. Labels wrap onto multiple lines when there are too many to fit on one\n\t\t\t\tvar lineWidths = me.lineWidths = [0];\n\t\t\t\tvar totalHeight = me.legendItems.length ? fontSize + (labelOpts.padding) : 0;\n\n\t\t\t\tctx.textAlign = 'left';\n\t\t\t\tctx.textBaseline = 'top';\n\n\t\t\t\thelpers.each(me.legendItems, function(legendItem, i) {\n\t\t\t\t\tvar boxWidth = getBoxWidth(labelOpts, fontSize);\n\t\t\t\t\tvar width = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;\n\n\t\t\t\t\tif (lineWidths[lineWidths.length - 1] + width + labelOpts.padding >= me.width) {\n\t\t\t\t\t\ttotalHeight += fontSize + (labelOpts.padding);\n\t\t\t\t\t\tlineWidths[lineWidths.length] = me.left;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Store the hitbox width and height here. Final position will be updated in `draw`\n\t\t\t\t\thitboxes[i] = {\n\t\t\t\t\t\tleft: 0,\n\t\t\t\t\t\ttop: 0,\n\t\t\t\t\t\twidth: width,\n\t\t\t\t\t\theight: fontSize\n\t\t\t\t\t};\n\n\t\t\t\t\tlineWidths[lineWidths.length - 1] += width + labelOpts.padding;\n\t\t\t\t});\n\n\t\t\t\tminSize.height += totalHeight;\n\n\t\t\t} else {\n\t\t\t\tvar vPadding = labelOpts.padding;\n\t\t\t\tvar columnWidths = me.columnWidths = [];\n\t\t\t\tvar totalWidth = labelOpts.padding;\n\t\t\t\tvar currentColWidth = 0;\n\t\t\t\tvar currentColHeight = 0;\n\t\t\t\tvar itemHeight = fontSize + vPadding;\n\n\t\t\t\thelpers.each(me.legendItems, function(legendItem, i) {\n\t\t\t\t\tvar boxWidth = getBoxWidth(labelOpts, fontSize);\n\t\t\t\t\tvar itemWidth = boxWidth + (fontSize / 2) + ctx.measureText(legendItem.text).width;\n\n\t\t\t\t\t// If too tall, go to new column\n\t\t\t\t\tif (currentColHeight + itemHeight > minSize.height) {\n\t\t\t\t\t\ttotalWidth += currentColWidth + labelOpts.padding;\n\t\t\t\t\t\tcolumnWidths.push(currentColWidth); // previous column width\n\n\t\t\t\t\t\tcurrentColWidth = 0;\n\t\t\t\t\t\tcurrentColHeight = 0;\n\t\t\t\t\t}\n\n\t\t\t\t\t// Get max width\n\t\t\t\t\tcurrentColWidth = Math.max(currentColWidth, itemWidth);\n\t\t\t\t\tcurrentColHeight += itemHeight;\n\n\t\t\t\t\t// Store the hitbox width and height here. Final position will be updated in `draw`\n\t\t\t\t\thitboxes[i] = {\n\t\t\t\t\t\tleft: 0,\n\t\t\t\t\t\ttop: 0,\n\t\t\t\t\t\twidth: itemWidth,\n\t\t\t\t\t\theight: fontSize\n\t\t\t\t\t};\n\t\t\t\t});\n\n\t\t\t\ttotalWidth += currentColWidth;\n\t\t\t\tcolumnWidths.push(currentColWidth);\n\t\t\t\tminSize.width += totalWidth;\n\t\t\t}\n\t\t}\n\n\t\tme.width = minSize.width;\n\t\tme.height = minSize.height;\n\t},\n\tafterFit: noop,\n\n\t// Shared Methods\n\tisHorizontal: function() {\n\t\treturn this.options.position === 'top' || this.options.position === 'bottom';\n\t},\n\n\t// Actually draw the legend on the canvas\n\tdraw: function() {\n\t\tvar me = this;\n\t\tvar opts = me.options;\n\t\tvar labelOpts = opts.labels;\n\t\tvar globalDefault = defaults.global;\n\t\tvar lineDefault = globalDefault.elements.line;\n\t\tvar legendWidth = me.width;\n\t\tvar lineWidths = me.lineWidths;\n\n\t\tif (opts.display) {\n\t\t\tvar ctx = me.ctx;\n\t\t\tvar valueOrDefault = helpers.valueOrDefault;\n\t\t\tvar fontColor = valueOrDefault(labelOpts.fontColor, globalDefault.defaultFontColor);\n\t\t\tvar fontSize = valueOrDefault(labelOpts.fontSize, globalDefault.defaultFontSize);\n\t\t\tvar fontStyle = valueOrDefault(labelOpts.fontStyle, globalDefault.defaultFontStyle);\n\t\t\tvar fontFamily = valueOrDefault(labelOpts.fontFamily, globalDefault.defaultFontFamily);\n\t\t\tvar labelFont = helpers.fontString(fontSize, fontStyle, fontFamily);\n\t\t\tvar cursor;\n\n\t\t\t// Canvas setup\n\t\t\tctx.textAlign = 'left';\n\t\t\tctx.textBaseline = 'middle';\n\t\t\tctx.lineWidth = 0.5;\n\t\t\tctx.strokeStyle = fontColor; // for strikethrough effect\n\t\t\tctx.fillStyle = fontColor; // render in correct colour\n\t\t\tctx.font = labelFont;\n\n\t\t\tvar boxWidth = getBoxWidth(labelOpts, fontSize);\n\t\t\tvar hitboxes = me.legendHitBoxes;\n\n\t\t\t// current position\n\t\t\tvar drawLegendBox = function(x, y, legendItem) {\n\t\t\t\tif (isNaN(boxWidth) || boxWidth <= 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Set the ctx for the box\n\t\t\t\tctx.save();\n\n\t\t\t\tctx.fillStyle = valueOrDefault(legendItem.fillStyle, globalDefault.defaultColor);\n\t\t\t\tctx.lineCap = valueOrDefault(legendItem.lineCap, lineDefault.borderCapStyle);\n\t\t\t\tctx.lineDashOffset = valueOrDefault(legendItem.lineDashOffset, lineDefault.borderDashOffset);\n\t\t\t\tctx.lineJoin = valueOrDefault(legendItem.lineJoin, lineDefault.borderJoinStyle);\n\t\t\t\tctx.lineWidth = valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth);\n\t\t\t\tctx.strokeStyle = valueOrDefault(legendItem.strokeStyle, globalDefault.defaultColor);\n\t\t\t\tvar isLineWidthZero = (valueOrDefault(legendItem.lineWidth, lineDefault.borderWidth) === 0);\n\n\t\t\t\tif (ctx.setLineDash) {\n\t\t\t\t\t// IE 9 and 10 do not support line dash\n\t\t\t\t\tctx.setLineDash(valueOrDefault(legendItem.lineDash, lineDefault.borderDash));\n\t\t\t\t}\n\n\t\t\t\tif (opts.labels && opts.labels.usePointStyle) {\n\t\t\t\t\t// Recalculate x and y for drawPoint() because its expecting\n\t\t\t\t\t// x and y to be center of figure (instead of top left)\n\t\t\t\t\tvar radius = fontSize * Math.SQRT2 / 2;\n\t\t\t\t\tvar offSet = radius / Math.SQRT2;\n\t\t\t\t\tvar centerX = x + offSet;\n\t\t\t\t\tvar centerY = y + offSet;\n\n\t\t\t\t\t// Draw pointStyle as legend symbol\n\t\t\t\t\thelpers.canvas.drawPoint(ctx, legendItem.pointStyle, radius, centerX, centerY);\n\t\t\t\t} else {\n\t\t\t\t\t// Draw box as legend symbol\n\t\t\t\t\tif (!isLineWidthZero) {\n\t\t\t\t\t\tctx.strokeRect(x, y, boxWidth, fontSize);\n\t\t\t\t\t}\n\t\t\t\t\tctx.fillRect(x, y, boxWidth, fontSize);\n\t\t\t\t}\n\n\t\t\t\tctx.restore();\n\t\t\t};\n\t\t\tvar fillText = function(x, y, legendItem, textWidth) {\n\t\t\t\tvar halfFontSize = fontSize / 2;\n\t\t\t\tvar xLeft = boxWidth + halfFontSize + x;\n\t\t\t\tvar yMiddle = y + halfFontSize;\n\n\t\t\t\tctx.fillText(legendItem.text, xLeft, yMiddle);\n\n\t\t\t\tif (legendItem.hidden) {\n\t\t\t\t\t// Strikethrough the text if hidden\n\t\t\t\t\tctx.beginPath();\n\t\t\t\t\tctx.lineWidth = 2;\n\t\t\t\t\tctx.moveTo(xLeft, yMiddle);\n\t\t\t\t\tctx.lineTo(xLeft + textWidth, yMiddle);\n\t\t\t\t\tctx.stroke();\n\t\t\t\t}\n\t\t\t};\n\n\t\t\t// Horizontal\n\t\t\tvar isHorizontal = me.isHorizontal();\n\t\t\tif (isHorizontal) {\n\t\t\t\tcursor = {\n\t\t\t\t\tx: me.left + ((legendWidth - lineWidths[0]) / 2),\n\t\t\t\t\ty: me.top + labelOpts.padding,\n\t\t\t\t\tline: 0\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tcursor = {\n\t\t\t\t\tx: me.left + labelOpts.padding,\n\t\t\t\t\ty: me.top + labelOpts.padding,\n\t\t\t\t\tline: 0\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tvar itemHeight = fontSize + labelOpts.padding;\n\t\t\thelpers.each(me.legendItems, function(legendItem, i) {\n\t\t\t\tvar textWidth = ctx.measureText(legendItem.text).width;\n\t\t\t\tvar width = boxWidth + (fontSize / 2) + textWidth;\n\t\t\t\tvar x = cursor.x;\n\t\t\t\tvar y = cursor.y;\n\n\t\t\t\tif (isHorizontal) {\n\t\t\t\t\tif (x + width >= legendWidth) {\n\t\t\t\t\t\ty = cursor.y += itemHeight;\n\t\t\t\t\t\tcursor.line++;\n\t\t\t\t\t\tx = cursor.x = me.left + ((legendWidth - lineWidths[cursor.line]) / 2);\n\t\t\t\t\t}\n\t\t\t\t} else if (y + itemHeight > me.bottom) {\n\t\t\t\t\tx = cursor.x = x + me.columnWidths[cursor.line] + labelOpts.padding;\n\t\t\t\t\ty = cursor.y = me.top + labelOpts.padding;\n\t\t\t\t\tcursor.line++;\n\t\t\t\t}\n\n\t\t\t\tdrawLegendBox(x, y, legendItem);\n\n\t\t\t\thitboxes[i].left = x;\n\t\t\t\thitboxes[i].top = y;\n\n\t\t\t\t// Fill the actual label\n\t\t\t\tfillText(x, y, legendItem, textWidth);\n\n\t\t\t\tif (isHorizontal) {\n\t\t\t\t\tcursor.x += width + (labelOpts.padding);\n\t\t\t\t} else {\n\t\t\t\t\tcursor.y += itemHeight;\n\t\t\t\t}\n\n\t\t\t});\n\t\t}\n\t},\n\n\t/**\n\t * Handle an event\n\t * @private\n\t * @param {IEvent} event - The event to handle\n\t * @return {Boolean} true if a change occured\n\t */\n\thandleEvent: function(e) {\n\t\tvar me = this;\n\t\tvar opts = me.options;\n\t\tvar type = e.type === 'mouseup' ? 'click' : e.type;\n\t\tvar changed = false;\n\n\t\tif (type === 'mousemove') {\n\t\t\tif (!opts.onHover) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else if (type === 'click') {\n\t\t\tif (!opts.onClick) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t} else {\n\t\t\treturn;\n\t\t}\n\n\t\t// Chart event already has relative position in it\n\t\tvar x = e.x;\n\t\tvar y = e.y;\n\n\t\tif (x >= me.left && x <= me.right && y >= me.top && y <= me.bottom) {\n\t\t\t// See if we are touching one of the dataset boxes\n\t\t\tvar lh = me.legendHitBoxes;\n\t\t\tfor (var i = 0; i < lh.length; ++i) {\n\t\t\t\tvar hitBox = lh[i];\n\n\t\t\t\tif (x >= hitBox.left && x <= hitBox.left + hitBox.width && y >= hitBox.top && y <= hitBox.top + hitBox.height) {\n\t\t\t\t\t// Touching an element\n\t\t\t\t\tif (type === 'click') {\n\t\t\t\t\t\t// use e.native for backwards compatibility\n\t\t\t\t\t\topts.onClick.call(me, e.native, me.legendItems[i]);\n\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} else if (type === 'mousemove') {\n\t\t\t\t\t\t// use e.native for backwards compatibility\n\t\t\t\t\t\topts.onHover.call(me, e.native, me.legendItems[i]);\n\t\t\t\t\t\tchanged = true;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn changed;\n\t}\n});\n\nfunction createNewLegendAndAttach(chart, legendOpts) {\n\tvar legend = new Legend({\n\t\tctx: chart.ctx,\n\t\toptions: legendOpts,\n\t\tchart: chart\n\t});\n\n\tlayouts.configure(chart, legend, legendOpts);\n\tlayouts.addBox(chart, legend);\n\tchart.legend = legend;\n}\n\nmodule.exports = {\n\tid: 'legend',\n\n\t/**\n\t * Backward compatibility: since 2.1.5, the legend is registered as a plugin, making\n\t * Chart.Legend obsolete. To avoid a breaking change, we export the Legend as part of\n\t * the plugin, which one will be re-exposed in the chart.js file.\n\t * https://github.com/chartjs/Chart.js/pull/2640\n\t * @private\n\t */\n\t_element: Legend,\n\n\tbeforeInit: function(chart) {\n\t\tvar legendOpts = chart.options.legend;\n\n\t\tif (legendOpts) {\n\t\t\tcreateNewLegendAndAttach(chart, legendOpts);\n\t\t}\n\t},\n\n\tbeforeUpdate: function(chart) {\n\t\tvar legendOpts = chart.options.legend;\n\t\tvar legend = chart.legend;\n\n\t\tif (legendOpts) {\n\t\t\thelpers.mergeIf(legendOpts, defaults.global.legend);\n\n\t\t\tif (legend) {\n\t\t\t\tlayouts.configure(chart, legend, legendOpts);\n\t\t\t\tlegend.options = legendOpts;\n\t\t\t} else {\n\t\t\t\tcreateNewLegendAndAttach(chart, legendOpts);\n\t\t\t}\n\t\t} else if (legend) {\n\t\t\tlayouts.removeBox(chart, legend);\n\t\t\tdelete chart.legend;\n\t\t}\n\t},\n\n\tafterEvent: function(chart, e) {\n\t\tvar legend = chart.legend;\n\t\tif (legend) {\n\t\t\tlegend.handleEvent(e);\n\t\t}\n\t}\n};\n\n},{\"25\":25,\"26\":26,\"30\":30,\"45\":45}],52:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\nvar Element = require(26);\nvar helpers = require(45);\nvar layouts = require(30);\n\nvar noop = helpers.noop;\n\ndefaults._set('global', {\n\ttitle: {\n\t\tdisplay: false,\n\t\tfontStyle: 'bold',\n\t\tfullWidth: true,\n\t\tlineHeight: 1.2,\n\t\tpadding: 10,\n\t\tposition: 'top',\n\t\ttext: '',\n\t\tweight: 2000         // by default greater than legend (1000) to be above\n\t}\n});\n\n/**\n * IMPORTANT: this class is exposed publicly as Chart.Legend, backward compatibility required!\n */\nvar Title = Element.extend({\n\tinitialize: function(config) {\n\t\tvar me = this;\n\t\thelpers.extend(me, config);\n\n\t\t// Contains hit boxes for each dataset (in dataset order)\n\t\tme.legendHitBoxes = [];\n\t},\n\n\t// These methods are ordered by lifecycle. Utilities then follow.\n\n\tbeforeUpdate: noop,\n\tupdate: function(maxWidth, maxHeight, margins) {\n\t\tvar me = this;\n\n\t\t// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)\n\t\tme.beforeUpdate();\n\n\t\t// Absorb the master measurements\n\t\tme.maxWidth = maxWidth;\n\t\tme.maxHeight = maxHeight;\n\t\tme.margins = margins;\n\n\t\t// Dimensions\n\t\tme.beforeSetDimensions();\n\t\tme.setDimensions();\n\t\tme.afterSetDimensions();\n\t\t// Labels\n\t\tme.beforeBuildLabels();\n\t\tme.buildLabels();\n\t\tme.afterBuildLabels();\n\n\t\t// Fit\n\t\tme.beforeFit();\n\t\tme.fit();\n\t\tme.afterFit();\n\t\t//\n\t\tme.afterUpdate();\n\n\t\treturn me.minSize;\n\n\t},\n\tafterUpdate: noop,\n\n\t//\n\n\tbeforeSetDimensions: noop,\n\tsetDimensions: function() {\n\t\tvar me = this;\n\t\t// Set the unconstrained dimension before label rotation\n\t\tif (me.isHorizontal()) {\n\t\t\t// Reset position before calculating rotation\n\t\t\tme.width = me.maxWidth;\n\t\t\tme.left = 0;\n\t\t\tme.right = me.width;\n\t\t} else {\n\t\t\tme.height = me.maxHeight;\n\n\t\t\t// Reset position before calculating rotation\n\t\t\tme.top = 0;\n\t\t\tme.bottom = me.height;\n\t\t}\n\n\t\t// Reset padding\n\t\tme.paddingLeft = 0;\n\t\tme.paddingTop = 0;\n\t\tme.paddingRight = 0;\n\t\tme.paddingBottom = 0;\n\n\t\t// Reset minSize\n\t\tme.minSize = {\n\t\t\twidth: 0,\n\t\t\theight: 0\n\t\t};\n\t},\n\tafterSetDimensions: noop,\n\n\t//\n\n\tbeforeBuildLabels: noop,\n\tbuildLabels: noop,\n\tafterBuildLabels: noop,\n\n\t//\n\n\tbeforeFit: noop,\n\tfit: function() {\n\t\tvar me = this;\n\t\tvar valueOrDefault = helpers.valueOrDefault;\n\t\tvar opts = me.options;\n\t\tvar display = opts.display;\n\t\tvar fontSize = valueOrDefault(opts.fontSize, defaults.global.defaultFontSize);\n\t\tvar minSize = me.minSize;\n\t\tvar lineCount = helpers.isArray(opts.text) ? opts.text.length : 1;\n\t\tvar lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize);\n\t\tvar textSize = display ? (lineCount * lineHeight) + (opts.padding * 2) : 0;\n\n\t\tif (me.isHorizontal()) {\n\t\t\tminSize.width = me.maxWidth; // fill all the width\n\t\t\tminSize.height = textSize;\n\t\t} else {\n\t\t\tminSize.width = textSize;\n\t\t\tminSize.height = me.maxHeight; // fill all the height\n\t\t}\n\n\t\tme.width = minSize.width;\n\t\tme.height = minSize.height;\n\n\t},\n\tafterFit: noop,\n\n\t// Shared Methods\n\tisHorizontal: function() {\n\t\tvar pos = this.options.position;\n\t\treturn pos === 'top' || pos === 'bottom';\n\t},\n\n\t// Actually draw the title block on the canvas\n\tdraw: function() {\n\t\tvar me = this;\n\t\tvar ctx = me.ctx;\n\t\tvar valueOrDefault = helpers.valueOrDefault;\n\t\tvar opts = me.options;\n\t\tvar globalDefaults = defaults.global;\n\n\t\tif (opts.display) {\n\t\t\tvar fontSize = valueOrDefault(opts.fontSize, globalDefaults.defaultFontSize);\n\t\t\tvar fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle);\n\t\t\tvar fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily);\n\t\t\tvar titleFont = helpers.fontString(fontSize, fontStyle, fontFamily);\n\t\t\tvar lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize);\n\t\t\tvar offset = lineHeight / 2 + opts.padding;\n\t\t\tvar rotation = 0;\n\t\t\tvar top = me.top;\n\t\t\tvar left = me.left;\n\t\t\tvar bottom = me.bottom;\n\t\t\tvar right = me.right;\n\t\t\tvar maxWidth, titleX, titleY;\n\n\t\t\tctx.fillStyle = valueOrDefault(opts.fontColor, globalDefaults.defaultFontColor); // render in correct colour\n\t\t\tctx.font = titleFont;\n\n\t\t\t// Horizontal\n\t\t\tif (me.isHorizontal()) {\n\t\t\t\ttitleX = left + ((right - left) / 2); // midpoint of the width\n\t\t\t\ttitleY = top + offset;\n\t\t\t\tmaxWidth = right - left;\n\t\t\t} else {\n\t\t\t\ttitleX = opts.position === 'left' ? left + offset : right - offset;\n\t\t\t\ttitleY = top + ((bottom - top) / 2);\n\t\t\t\tmaxWidth = bottom - top;\n\t\t\t\trotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);\n\t\t\t}\n\n\t\t\tctx.save();\n\t\t\tctx.translate(titleX, titleY);\n\t\t\tctx.rotate(rotation);\n\t\t\tctx.textAlign = 'center';\n\t\t\tctx.textBaseline = 'middle';\n\n\t\t\tvar text = opts.text;\n\t\t\tif (helpers.isArray(text)) {\n\t\t\t\tvar y = 0;\n\t\t\t\tfor (var i = 0; i < text.length; ++i) {\n\t\t\t\t\tctx.fillText(text[i], 0, y, maxWidth);\n\t\t\t\t\ty += lineHeight;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tctx.fillText(text, 0, 0, maxWidth);\n\t\t\t}\n\n\t\t\tctx.restore();\n\t\t}\n\t}\n});\n\nfunction createNewTitleBlockAndAttach(chart, titleOpts) {\n\tvar title = new Title({\n\t\tctx: chart.ctx,\n\t\toptions: titleOpts,\n\t\tchart: chart\n\t});\n\n\tlayouts.configure(chart, title, titleOpts);\n\tlayouts.addBox(chart, title);\n\tchart.titleBlock = title;\n}\n\nmodule.exports = {\n\tid: 'title',\n\n\t/**\n\t * Backward compatibility: since 2.1.5, the title is registered as a plugin, making\n\t * Chart.Title obsolete. To avoid a breaking change, we export the Title as part of\n\t * the plugin, which one will be re-exposed in the chart.js file.\n\t * https://github.com/chartjs/Chart.js/pull/2640\n\t * @private\n\t */\n\t_element: Title,\n\n\tbeforeInit: function(chart) {\n\t\tvar titleOpts = chart.options.title;\n\n\t\tif (titleOpts) {\n\t\t\tcreateNewTitleBlockAndAttach(chart, titleOpts);\n\t\t}\n\t},\n\n\tbeforeUpdate: function(chart) {\n\t\tvar titleOpts = chart.options.title;\n\t\tvar titleBlock = chart.titleBlock;\n\n\t\tif (titleOpts) {\n\t\t\thelpers.mergeIf(titleOpts, defaults.global.title);\n\n\t\t\tif (titleBlock) {\n\t\t\t\tlayouts.configure(chart, titleBlock, titleOpts);\n\t\t\t\ttitleBlock.options = titleOpts;\n\t\t\t} else {\n\t\t\t\tcreateNewTitleBlockAndAttach(chart, titleOpts);\n\t\t\t}\n\t\t} else if (titleBlock) {\n\t\t\tlayouts.removeBox(chart, titleBlock);\n\t\t\tdelete chart.titleBlock;\n\t\t}\n\t}\n};\n\n},{\"25\":25,\"26\":26,\"30\":30,\"45\":45}],53:[function(require,module,exports){\n'use strict';\n\nmodule.exports = function(Chart) {\n\n\t// Default config for a category scale\n\tvar defaultConfig = {\n\t\tposition: 'bottom'\n\t};\n\n\tvar DatasetScale = Chart.Scale.extend({\n\t\t/**\n\t\t* Internal function to get the correct labels. If data.xLabels or data.yLabels are defined, use those\n\t\t* else fall back to data.labels\n\t\t* @private\n\t\t*/\n\t\tgetLabels: function() {\n\t\t\tvar data = this.chart.data;\n\t\t\treturn this.options.labels || (this.isHorizontal() ? data.xLabels : data.yLabels) || data.labels;\n\t\t},\n\n\t\tdetermineDataLimits: function() {\n\t\t\tvar me = this;\n\t\t\tvar labels = me.getLabels();\n\t\t\tme.minIndex = 0;\n\t\t\tme.maxIndex = labels.length - 1;\n\t\t\tvar findIndex;\n\n\t\t\tif (me.options.ticks.min !== undefined) {\n\t\t\t\t// user specified min value\n\t\t\t\tfindIndex = labels.indexOf(me.options.ticks.min);\n\t\t\t\tme.minIndex = findIndex !== -1 ? findIndex : me.minIndex;\n\t\t\t}\n\n\t\t\tif (me.options.ticks.max !== undefined) {\n\t\t\t\t// user specified max value\n\t\t\t\tfindIndex = labels.indexOf(me.options.ticks.max);\n\t\t\t\tme.maxIndex = findIndex !== -1 ? findIndex : me.maxIndex;\n\t\t\t}\n\n\t\t\tme.min = labels[me.minIndex];\n\t\t\tme.max = labels[me.maxIndex];\n\t\t},\n\n\t\tbuildTicks: function() {\n\t\t\tvar me = this;\n\t\t\tvar labels = me.getLabels();\n\t\t\t// If we are viewing some subset of labels, slice the original array\n\t\t\tme.ticks = (me.minIndex === 0 && me.maxIndex === labels.length - 1) ? labels : labels.slice(me.minIndex, me.maxIndex + 1);\n\t\t},\n\n\t\tgetLabelForIndex: function(index, datasetIndex) {\n\t\t\tvar me = this;\n\t\t\tvar data = me.chart.data;\n\t\t\tvar isHorizontal = me.isHorizontal();\n\n\t\t\tif (data.yLabels && !isHorizontal) {\n\t\t\t\treturn me.getRightValue(data.datasets[datasetIndex].data[index]);\n\t\t\t}\n\t\t\treturn me.ticks[index - me.minIndex];\n\t\t},\n\n\t\t// Used to get data value locations.  Value can either be an index or a numerical value\n\t\tgetPixelForValue: function(value, index) {\n\t\t\tvar me = this;\n\t\t\tvar offset = me.options.offset;\n\t\t\t// 1 is added because we need the length but we have the indexes\n\t\t\tvar offsetAmt = Math.max((me.maxIndex + 1 - me.minIndex - (offset ? 0 : 1)), 1);\n\n\t\t\t// If value is a data object, then index is the index in the data array,\n\t\t\t// not the index of the scale. We need to change that.\n\t\t\tvar valueCategory;\n\t\t\tif (value !== undefined && value !== null) {\n\t\t\t\tvalueCategory = me.isHorizontal() ? value.x : value.y;\n\t\t\t}\n\t\t\tif (valueCategory !== undefined || (value !== undefined && isNaN(index))) {\n\t\t\t\tvar labels = me.getLabels();\n\t\t\t\tvalue = valueCategory || value;\n\t\t\t\tvar idx = labels.indexOf(value);\n\t\t\t\tindex = idx !== -1 ? idx : index;\n\t\t\t}\n\n\t\t\tif (me.isHorizontal()) {\n\t\t\t\tvar valueWidth = me.width / offsetAmt;\n\t\t\t\tvar widthOffset = (valueWidth * (index - me.minIndex));\n\n\t\t\t\tif (offset) {\n\t\t\t\t\twidthOffset += (valueWidth / 2);\n\t\t\t\t}\n\n\t\t\t\treturn me.left + Math.round(widthOffset);\n\t\t\t}\n\t\t\tvar valueHeight = me.height / offsetAmt;\n\t\t\tvar heightOffset = (valueHeight * (index - me.minIndex));\n\n\t\t\tif (offset) {\n\t\t\t\theightOffset += (valueHeight / 2);\n\t\t\t}\n\n\t\t\treturn me.top + Math.round(heightOffset);\n\t\t},\n\t\tgetPixelForTick: function(index) {\n\t\t\treturn this.getPixelForValue(this.ticks[index], index + this.minIndex, null);\n\t\t},\n\t\tgetValueForPixel: function(pixel) {\n\t\t\tvar me = this;\n\t\t\tvar offset = me.options.offset;\n\t\t\tvar value;\n\t\t\tvar offsetAmt = Math.max((me._ticks.length - (offset ? 0 : 1)), 1);\n\t\t\tvar horz = me.isHorizontal();\n\t\t\tvar valueDimension = (horz ? me.width : me.height) / offsetAmt;\n\n\t\t\tpixel -= horz ? me.left : me.top;\n\n\t\t\tif (offset) {\n\t\t\t\tpixel -= (valueDimension / 2);\n\t\t\t}\n\n\t\t\tif (pixel <= 0) {\n\t\t\t\tvalue = 0;\n\t\t\t} else {\n\t\t\t\tvalue = Math.round(pixel / valueDimension);\n\t\t\t}\n\n\t\t\treturn value + me.minIndex;\n\t\t},\n\t\tgetBasePixel: function() {\n\t\t\treturn this.bottom;\n\t\t}\n\t});\n\n\tChart.scaleService.registerScaleType('category', DatasetScale, defaultConfig);\n\n};\n\n},{}],54:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\nvar helpers = require(45);\nvar Ticks = require(34);\n\nmodule.exports = function(Chart) {\n\n\tvar defaultConfig = {\n\t\tposition: 'left',\n\t\tticks: {\n\t\t\tcallback: Ticks.formatters.linear\n\t\t}\n\t};\n\n\tvar LinearScale = Chart.LinearScaleBase.extend({\n\n\t\tdetermineDataLimits: function() {\n\t\t\tvar me = this;\n\t\t\tvar opts = me.options;\n\t\t\tvar chart = me.chart;\n\t\t\tvar data = chart.data;\n\t\t\tvar datasets = data.datasets;\n\t\t\tvar isHorizontal = me.isHorizontal();\n\t\t\tvar DEFAULT_MIN = 0;\n\t\t\tvar DEFAULT_MAX = 1;\n\n\t\t\tfunction IDMatches(meta) {\n\t\t\t\treturn isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;\n\t\t\t}\n\n\t\t\t// First Calculate the range\n\t\t\tme.min = null;\n\t\t\tme.max = null;\n\n\t\t\tvar hasStacks = opts.stacked;\n\t\t\tif (hasStacks === undefined) {\n\t\t\t\thelpers.each(datasets, function(dataset, datasetIndex) {\n\t\t\t\t\tif (hasStacks) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tvar meta = chart.getDatasetMeta(datasetIndex);\n\t\t\t\t\tif (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&\n\t\t\t\t\t\tmeta.stack !== undefined) {\n\t\t\t\t\t\thasStacks = true;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (opts.stacked || hasStacks) {\n\t\t\t\tvar valuesPerStack = {};\n\n\t\t\t\thelpers.each(datasets, function(dataset, datasetIndex) {\n\t\t\t\t\tvar meta = chart.getDatasetMeta(datasetIndex);\n\t\t\t\t\tvar key = [\n\t\t\t\t\t\tmeta.type,\n\t\t\t\t\t\t// we have a separate stack for stack=undefined datasets when the opts.stacked is undefined\n\t\t\t\t\t\t((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),\n\t\t\t\t\t\tmeta.stack\n\t\t\t\t\t].join('.');\n\n\t\t\t\t\tif (valuesPerStack[key] === undefined) {\n\t\t\t\t\t\tvaluesPerStack[key] = {\n\t\t\t\t\t\t\tpositiveValues: [],\n\t\t\t\t\t\t\tnegativeValues: []\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Store these per type\n\t\t\t\t\tvar positiveValues = valuesPerStack[key].positiveValues;\n\t\t\t\t\tvar negativeValues = valuesPerStack[key].negativeValues;\n\n\t\t\t\t\tif (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {\n\t\t\t\t\t\thelpers.each(dataset.data, function(rawValue, index) {\n\t\t\t\t\t\t\tvar value = +me.getRightValue(rawValue);\n\t\t\t\t\t\t\tif (isNaN(value) || meta.data[index].hidden) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tpositiveValues[index] = positiveValues[index] || 0;\n\t\t\t\t\t\t\tnegativeValues[index] = negativeValues[index] || 0;\n\n\t\t\t\t\t\t\tif (opts.relativePoints) {\n\t\t\t\t\t\t\t\tpositiveValues[index] = 100;\n\t\t\t\t\t\t\t} else if (value < 0) {\n\t\t\t\t\t\t\t\tnegativeValues[index] += value;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tpositiveValues[index] += value;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\thelpers.each(valuesPerStack, function(valuesForType) {\n\t\t\t\t\tvar values = valuesForType.positiveValues.concat(valuesForType.negativeValues);\n\t\t\t\t\tvar minVal = helpers.min(values);\n\t\t\t\t\tvar maxVal = helpers.max(values);\n\t\t\t\t\tme.min = me.min === null ? minVal : Math.min(me.min, minVal);\n\t\t\t\t\tme.max = me.max === null ? maxVal : Math.max(me.max, maxVal);\n\t\t\t\t});\n\n\t\t\t} else {\n\t\t\t\thelpers.each(datasets, function(dataset, datasetIndex) {\n\t\t\t\t\tvar meta = chart.getDatasetMeta(datasetIndex);\n\t\t\t\t\tif (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {\n\t\t\t\t\t\thelpers.each(dataset.data, function(rawValue, index) {\n\t\t\t\t\t\t\tvar value = +me.getRightValue(rawValue);\n\t\t\t\t\t\t\tif (isNaN(value) || meta.data[index].hidden) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (me.min === null) {\n\t\t\t\t\t\t\t\tme.min = value;\n\t\t\t\t\t\t\t} else if (value < me.min) {\n\t\t\t\t\t\t\t\tme.min = value;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (me.max === null) {\n\t\t\t\t\t\t\t\tme.max = value;\n\t\t\t\t\t\t\t} else if (value > me.max) {\n\t\t\t\t\t\t\t\tme.max = value;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tme.min = isFinite(me.min) && !isNaN(me.min) ? me.min : DEFAULT_MIN;\n\t\t\tme.max = isFinite(me.max) && !isNaN(me.max) ? me.max : DEFAULT_MAX;\n\n\t\t\t// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero\n\t\t\tthis.handleTickRangeOptions();\n\t\t},\n\t\tgetTickLimit: function() {\n\t\t\tvar maxTicks;\n\t\t\tvar me = this;\n\t\t\tvar tickOpts = me.options.ticks;\n\n\t\t\tif (me.isHorizontal()) {\n\t\t\t\tmaxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.width / 50));\n\t\t\t} else {\n\t\t\t\t// The factor of 2 used to scale the font size has been experimentally determined.\n\t\t\t\tvar tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, defaults.global.defaultFontSize);\n\t\t\t\tmaxTicks = Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(me.height / (2 * tickFontSize)));\n\t\t\t}\n\n\t\t\treturn maxTicks;\n\t\t},\n\t\t// Called after the ticks are built. We need\n\t\thandleDirectionalChanges: function() {\n\t\t\tif (!this.isHorizontal()) {\n\t\t\t\t// We are in a vertical orientation. The top value is the highest. So reverse the array\n\t\t\t\tthis.ticks.reverse();\n\t\t\t}\n\t\t},\n\t\tgetLabelForIndex: function(index, datasetIndex) {\n\t\t\treturn +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);\n\t\t},\n\t\t// Utils\n\t\tgetPixelForValue: function(value) {\n\t\t\t// This must be called after fit has been run so that\n\t\t\t// this.left, this.top, this.right, and this.bottom have been defined\n\t\t\tvar me = this;\n\t\t\tvar start = me.start;\n\n\t\t\tvar rightValue = +me.getRightValue(value);\n\t\t\tvar pixel;\n\t\t\tvar range = me.end - start;\n\n\t\t\tif (me.isHorizontal()) {\n\t\t\t\tpixel = me.left + (me.width / range * (rightValue - start));\n\t\t\t} else {\n\t\t\t\tpixel = me.bottom - (me.height / range * (rightValue - start));\n\t\t\t}\n\t\t\treturn pixel;\n\t\t},\n\t\tgetValueForPixel: function(pixel) {\n\t\t\tvar me = this;\n\t\t\tvar isHorizontal = me.isHorizontal();\n\t\t\tvar innerDimension = isHorizontal ? me.width : me.height;\n\t\t\tvar offset = (isHorizontal ? pixel - me.left : me.bottom - pixel) / innerDimension;\n\t\t\treturn me.start + ((me.end - me.start) * offset);\n\t\t},\n\t\tgetPixelForTick: function(index) {\n\t\t\treturn this.getPixelForValue(this.ticksAsNumbers[index]);\n\t\t}\n\t});\n\tChart.scaleService.registerScaleType('linear', LinearScale, defaultConfig);\n\n};\n\n},{\"25\":25,\"34\":34,\"45\":45}],55:[function(require,module,exports){\n'use strict';\n\nvar helpers = require(45);\n\n/**\n * Generate a set of linear ticks\n * @param generationOptions the options used to generate the ticks\n * @param dataRange the range of the data\n * @returns {Array<Number>} array of tick values\n */\nfunction generateTicks(generationOptions, dataRange) {\n\tvar ticks = [];\n\t// To get a \"nice\" value for the tick spacing, we will use the appropriately named\n\t// \"nice number\" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks\n\t// for details.\n\n\tvar spacing;\n\tif (generationOptions.stepSize && generationOptions.stepSize > 0) {\n\t\tspacing = generationOptions.stepSize;\n\t} else {\n\t\tvar niceRange = helpers.niceNum(dataRange.max - dataRange.min, false);\n\t\tspacing = helpers.niceNum(niceRange / (generationOptions.maxTicks - 1), true);\n\t}\n\tvar niceMin = Math.floor(dataRange.min / spacing) * spacing;\n\tvar niceMax = Math.ceil(dataRange.max / spacing) * spacing;\n\n\t// If min, max and stepSize is set and they make an evenly spaced scale use it.\n\tif (generationOptions.min && generationOptions.max && generationOptions.stepSize) {\n\t\t// If very close to our whole number, use it.\n\t\tif (helpers.almostWhole((generationOptions.max - generationOptions.min) / generationOptions.stepSize, spacing / 1000)) {\n\t\t\tniceMin = generationOptions.min;\n\t\t\tniceMax = generationOptions.max;\n\t\t}\n\t}\n\n\tvar numSpaces = (niceMax - niceMin) / spacing;\n\t// If very close to our rounded value, use it.\n\tif (helpers.almostEquals(numSpaces, Math.round(numSpaces), spacing / 1000)) {\n\t\tnumSpaces = Math.round(numSpaces);\n\t} else {\n\t\tnumSpaces = Math.ceil(numSpaces);\n\t}\n\n\tvar precision = 1;\n\tif (spacing < 1) {\n\t\tprecision = Math.pow(10, spacing.toString().length - 2);\n\t\tniceMin = Math.round(niceMin * precision) / precision;\n\t\tniceMax = Math.round(niceMax * precision) / precision;\n\t}\n\tticks.push(generationOptions.min !== undefined ? generationOptions.min : niceMin);\n\tfor (var j = 1; j < numSpaces; ++j) {\n\t\tticks.push(Math.round((niceMin + j * spacing) * precision) / precision);\n\t}\n\tticks.push(generationOptions.max !== undefined ? generationOptions.max : niceMax);\n\n\treturn ticks;\n}\n\n\nmodule.exports = function(Chart) {\n\n\tvar noop = helpers.noop;\n\n\tChart.LinearScaleBase = Chart.Scale.extend({\n\t\tgetRightValue: function(value) {\n\t\t\tif (typeof value === 'string') {\n\t\t\t\treturn +value;\n\t\t\t}\n\t\t\treturn Chart.Scale.prototype.getRightValue.call(this, value);\n\t\t},\n\n\t\thandleTickRangeOptions: function() {\n\t\t\tvar me = this;\n\t\t\tvar opts = me.options;\n\t\t\tvar tickOpts = opts.ticks;\n\n\t\t\t// If we are forcing it to begin at 0, but 0 will already be rendered on the chart,\n\t\t\t// do nothing since that would make the chart weird. If the user really wants a weird chart\n\t\t\t// axis, they can manually override it\n\t\t\tif (tickOpts.beginAtZero) {\n\t\t\t\tvar minSign = helpers.sign(me.min);\n\t\t\t\tvar maxSign = helpers.sign(me.max);\n\n\t\t\t\tif (minSign < 0 && maxSign < 0) {\n\t\t\t\t\t// move the top up to 0\n\t\t\t\t\tme.max = 0;\n\t\t\t\t} else if (minSign > 0 && maxSign > 0) {\n\t\t\t\t\t// move the bottom down to 0\n\t\t\t\t\tme.min = 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar setMin = tickOpts.min !== undefined || tickOpts.suggestedMin !== undefined;\n\t\t\tvar setMax = tickOpts.max !== undefined || tickOpts.suggestedMax !== undefined;\n\n\t\t\tif (tickOpts.min !== undefined) {\n\t\t\t\tme.min = tickOpts.min;\n\t\t\t} else if (tickOpts.suggestedMin !== undefined) {\n\t\t\t\tif (me.min === null) {\n\t\t\t\t\tme.min = tickOpts.suggestedMin;\n\t\t\t\t} else {\n\t\t\t\t\tme.min = Math.min(me.min, tickOpts.suggestedMin);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (tickOpts.max !== undefined) {\n\t\t\t\tme.max = tickOpts.max;\n\t\t\t} else if (tickOpts.suggestedMax !== undefined) {\n\t\t\t\tif (me.max === null) {\n\t\t\t\t\tme.max = tickOpts.suggestedMax;\n\t\t\t\t} else {\n\t\t\t\t\tme.max = Math.max(me.max, tickOpts.suggestedMax);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (setMin !== setMax) {\n\t\t\t\t// We set the min or the max but not both.\n\t\t\t\t// So ensure that our range is good\n\t\t\t\t// Inverted or 0 length range can happen when\n\t\t\t\t// ticks.min is set, and no datasets are visible\n\t\t\t\tif (me.min >= me.max) {\n\t\t\t\t\tif (setMin) {\n\t\t\t\t\t\tme.max = me.min + 1;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tme.min = me.max - 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (me.min === me.max) {\n\t\t\t\tme.max++;\n\n\t\t\t\tif (!tickOpts.beginAtZero) {\n\t\t\t\t\tme.min--;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tgetTickLimit: noop,\n\t\thandleDirectionalChanges: noop,\n\n\t\tbuildTicks: function() {\n\t\t\tvar me = this;\n\t\t\tvar opts = me.options;\n\t\t\tvar tickOpts = opts.ticks;\n\n\t\t\t// Figure out what the max number of ticks we can support it is based on the size of\n\t\t\t// the axis area. For now, we say that the minimum tick spacing in pixels must be 50\n\t\t\t// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on\n\t\t\t// the graph. Make sure we always have at least 2 ticks\n\t\t\tvar maxTicks = me.getTickLimit();\n\t\t\tmaxTicks = Math.max(2, maxTicks);\n\n\t\t\tvar numericGeneratorOptions = {\n\t\t\t\tmaxTicks: maxTicks,\n\t\t\t\tmin: tickOpts.min,\n\t\t\t\tmax: tickOpts.max,\n\t\t\t\tstepSize: helpers.valueOrDefault(tickOpts.fixedStepSize, tickOpts.stepSize)\n\t\t\t};\n\t\t\tvar ticks = me.ticks = generateTicks(numericGeneratorOptions, me);\n\n\t\t\tme.handleDirectionalChanges();\n\n\t\t\t// At this point, we need to update our max and min given the tick values since we have expanded the\n\t\t\t// range of the scale\n\t\t\tme.max = helpers.max(ticks);\n\t\t\tme.min = helpers.min(ticks);\n\n\t\t\tif (tickOpts.reverse) {\n\t\t\t\tticks.reverse();\n\n\t\t\t\tme.start = me.max;\n\t\t\t\tme.end = me.min;\n\t\t\t} else {\n\t\t\t\tme.start = me.min;\n\t\t\t\tme.end = me.max;\n\t\t\t}\n\t\t},\n\t\tconvertTicksToLabels: function() {\n\t\t\tvar me = this;\n\t\t\tme.ticksAsNumbers = me.ticks.slice();\n\t\t\tme.zeroLineIndex = me.ticks.indexOf(0);\n\n\t\t\tChart.Scale.prototype.convertTicksToLabels.call(me);\n\t\t}\n\t});\n};\n\n},{\"45\":45}],56:[function(require,module,exports){\n'use strict';\n\nvar helpers = require(45);\nvar Ticks = require(34);\n\n/**\n * Generate a set of logarithmic ticks\n * @param generationOptions the options used to generate the ticks\n * @param dataRange the range of the data\n * @returns {Array<Number>} array of tick values\n */\nfunction generateTicks(generationOptions, dataRange) {\n\tvar ticks = [];\n\tvar valueOrDefault = helpers.valueOrDefault;\n\n\t// Figure out what the max number of ticks we can support it is based on the size of\n\t// the axis area. For now, we say that the minimum tick spacing in pixels must be 50\n\t// We also limit the maximum number of ticks to 11 which gives a nice 10 squares on\n\t// the graph\n\tvar tickVal = valueOrDefault(generationOptions.min, Math.pow(10, Math.floor(helpers.log10(dataRange.min))));\n\n\tvar endExp = Math.floor(helpers.log10(dataRange.max));\n\tvar endSignificand = Math.ceil(dataRange.max / Math.pow(10, endExp));\n\tvar exp, significand;\n\n\tif (tickVal === 0) {\n\t\texp = Math.floor(helpers.log10(dataRange.minNotZero));\n\t\tsignificand = Math.floor(dataRange.minNotZero / Math.pow(10, exp));\n\n\t\tticks.push(tickVal);\n\t\ttickVal = significand * Math.pow(10, exp);\n\t} else {\n\t\texp = Math.floor(helpers.log10(tickVal));\n\t\tsignificand = Math.floor(tickVal / Math.pow(10, exp));\n\t}\n\tvar precision = exp < 0 ? Math.pow(10, Math.abs(exp)) : 1;\n\n\tdo {\n\t\tticks.push(tickVal);\n\n\t\t++significand;\n\t\tif (significand === 10) {\n\t\t\tsignificand = 1;\n\t\t\t++exp;\n\t\t\tprecision = exp >= 0 ? 1 : precision;\n\t\t}\n\n\t\ttickVal = Math.round(significand * Math.pow(10, exp) * precision) / precision;\n\t} while (exp < endExp || (exp === endExp && significand < endSignificand));\n\n\tvar lastTick = valueOrDefault(generationOptions.max, tickVal);\n\tticks.push(lastTick);\n\n\treturn ticks;\n}\n\n\nmodule.exports = function(Chart) {\n\n\tvar defaultConfig = {\n\t\tposition: 'left',\n\n\t\t// label settings\n\t\tticks: {\n\t\t\tcallback: Ticks.formatters.logarithmic\n\t\t}\n\t};\n\n\tvar LogarithmicScale = Chart.Scale.extend({\n\t\tdetermineDataLimits: function() {\n\t\t\tvar me = this;\n\t\t\tvar opts = me.options;\n\t\t\tvar chart = me.chart;\n\t\t\tvar data = chart.data;\n\t\t\tvar datasets = data.datasets;\n\t\t\tvar isHorizontal = me.isHorizontal();\n\t\t\tfunction IDMatches(meta) {\n\t\t\t\treturn isHorizontal ? meta.xAxisID === me.id : meta.yAxisID === me.id;\n\t\t\t}\n\n\t\t\t// Calculate Range\n\t\t\tme.min = null;\n\t\t\tme.max = null;\n\t\t\tme.minNotZero = null;\n\n\t\t\tvar hasStacks = opts.stacked;\n\t\t\tif (hasStacks === undefined) {\n\t\t\t\thelpers.each(datasets, function(dataset, datasetIndex) {\n\t\t\t\t\tif (hasStacks) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tvar meta = chart.getDatasetMeta(datasetIndex);\n\t\t\t\t\tif (chart.isDatasetVisible(datasetIndex) && IDMatches(meta) &&\n\t\t\t\t\t\tmeta.stack !== undefined) {\n\t\t\t\t\t\thasStacks = true;\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (opts.stacked || hasStacks) {\n\t\t\t\tvar valuesPerStack = {};\n\n\t\t\t\thelpers.each(datasets, function(dataset, datasetIndex) {\n\t\t\t\t\tvar meta = chart.getDatasetMeta(datasetIndex);\n\t\t\t\t\tvar key = [\n\t\t\t\t\t\tmeta.type,\n\t\t\t\t\t\t// we have a separate stack for stack=undefined datasets when the opts.stacked is undefined\n\t\t\t\t\t\t((opts.stacked === undefined && meta.stack === undefined) ? datasetIndex : ''),\n\t\t\t\t\t\tmeta.stack\n\t\t\t\t\t].join('.');\n\n\t\t\t\t\tif (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {\n\t\t\t\t\t\tif (valuesPerStack[key] === undefined) {\n\t\t\t\t\t\t\tvaluesPerStack[key] = [];\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\thelpers.each(dataset.data, function(rawValue, index) {\n\t\t\t\t\t\t\tvar values = valuesPerStack[key];\n\t\t\t\t\t\t\tvar value = +me.getRightValue(rawValue);\n\t\t\t\t\t\t\t// invalid, hidden and negative values are ignored\n\t\t\t\t\t\t\tif (isNaN(value) || meta.data[index].hidden || value < 0) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tvalues[index] = values[index] || 0;\n\t\t\t\t\t\t\tvalues[index] += value;\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\thelpers.each(valuesPerStack, function(valuesForType) {\n\t\t\t\t\tif (valuesForType.length > 0) {\n\t\t\t\t\t\tvar minVal = helpers.min(valuesForType);\n\t\t\t\t\t\tvar maxVal = helpers.max(valuesForType);\n\t\t\t\t\t\tme.min = me.min === null ? minVal : Math.min(me.min, minVal);\n\t\t\t\t\t\tme.max = me.max === null ? maxVal : Math.max(me.max, maxVal);\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t} else {\n\t\t\t\thelpers.each(datasets, function(dataset, datasetIndex) {\n\t\t\t\t\tvar meta = chart.getDatasetMeta(datasetIndex);\n\t\t\t\t\tif (chart.isDatasetVisible(datasetIndex) && IDMatches(meta)) {\n\t\t\t\t\t\thelpers.each(dataset.data, function(rawValue, index) {\n\t\t\t\t\t\t\tvar value = +me.getRightValue(rawValue);\n\t\t\t\t\t\t\t// invalid, hidden and negative values are ignored\n\t\t\t\t\t\t\tif (isNaN(value) || meta.data[index].hidden || value < 0) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (me.min === null) {\n\t\t\t\t\t\t\t\tme.min = value;\n\t\t\t\t\t\t\t} else if (value < me.min) {\n\t\t\t\t\t\t\t\tme.min = value;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (me.max === null) {\n\t\t\t\t\t\t\t\tme.max = value;\n\t\t\t\t\t\t\t} else if (value > me.max) {\n\t\t\t\t\t\t\t\tme.max = value;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif (value !== 0 && (me.minNotZero === null || value < me.minNotZero)) {\n\t\t\t\t\t\t\t\tme.minNotZero = value;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Common base implementation to handle ticks.min, ticks.max\n\t\t\tthis.handleTickRangeOptions();\n\t\t},\n\t\thandleTickRangeOptions: function() {\n\t\t\tvar me = this;\n\t\t\tvar opts = me.options;\n\t\t\tvar tickOpts = opts.ticks;\n\t\t\tvar valueOrDefault = helpers.valueOrDefault;\n\t\t\tvar DEFAULT_MIN = 1;\n\t\t\tvar DEFAULT_MAX = 10;\n\n\t\t\tme.min = valueOrDefault(tickOpts.min, me.min);\n\t\t\tme.max = valueOrDefault(tickOpts.max, me.max);\n\n\t\t\tif (me.min === me.max) {\n\t\t\t\tif (me.min !== 0 && me.min !== null) {\n\t\t\t\t\tme.min = Math.pow(10, Math.floor(helpers.log10(me.min)) - 1);\n\t\t\t\t\tme.max = Math.pow(10, Math.floor(helpers.log10(me.max)) + 1);\n\t\t\t\t} else {\n\t\t\t\t\tme.min = DEFAULT_MIN;\n\t\t\t\t\tme.max = DEFAULT_MAX;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (me.min === null) {\n\t\t\t\tme.min = Math.pow(10, Math.floor(helpers.log10(me.max)) - 1);\n\t\t\t}\n\t\t\tif (me.max === null) {\n\t\t\t\tme.max = me.min !== 0\n\t\t\t\t\t? Math.pow(10, Math.floor(helpers.log10(me.min)) + 1)\n\t\t\t\t\t: DEFAULT_MAX;\n\t\t\t}\n\t\t\tif (me.minNotZero === null) {\n\t\t\t\tif (me.min > 0) {\n\t\t\t\t\tme.minNotZero = me.min;\n\t\t\t\t} else if (me.max < 1) {\n\t\t\t\t\tme.minNotZero = Math.pow(10, Math.floor(helpers.log10(me.max)));\n\t\t\t\t} else {\n\t\t\t\t\tme.minNotZero = DEFAULT_MIN;\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tbuildTicks: function() {\n\t\t\tvar me = this;\n\t\t\tvar opts = me.options;\n\t\t\tvar tickOpts = opts.ticks;\n\t\t\tvar reverse = !me.isHorizontal();\n\n\t\t\tvar generationOptions = {\n\t\t\t\tmin: tickOpts.min,\n\t\t\t\tmax: tickOpts.max\n\t\t\t};\n\t\t\tvar ticks = me.ticks = generateTicks(generationOptions, me);\n\n\t\t\t// At this point, we need to update our max and min given the tick values since we have expanded the\n\t\t\t// range of the scale\n\t\t\tme.max = helpers.max(ticks);\n\t\t\tme.min = helpers.min(ticks);\n\n\t\t\tif (tickOpts.reverse) {\n\t\t\t\treverse = !reverse;\n\t\t\t\tme.start = me.max;\n\t\t\t\tme.end = me.min;\n\t\t\t} else {\n\t\t\t\tme.start = me.min;\n\t\t\t\tme.end = me.max;\n\t\t\t}\n\t\t\tif (reverse) {\n\t\t\t\tticks.reverse();\n\t\t\t}\n\t\t},\n\t\tconvertTicksToLabels: function() {\n\t\t\tthis.tickValues = this.ticks.slice();\n\n\t\t\tChart.Scale.prototype.convertTicksToLabels.call(this);\n\t\t},\n\t\t// Get the correct tooltip label\n\t\tgetLabelForIndex: function(index, datasetIndex) {\n\t\t\treturn +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);\n\t\t},\n\t\tgetPixelForTick: function(index) {\n\t\t\treturn this.getPixelForValue(this.tickValues[index]);\n\t\t},\n\t\t/**\n\t\t * Returns the value of the first tick.\n\t\t * @param {Number} value - The minimum not zero value.\n\t\t * @return {Number} The first tick value.\n\t\t * @private\n\t\t */\n\t\t_getFirstTickValue: function(value) {\n\t\t\tvar exp = Math.floor(helpers.log10(value));\n\t\t\tvar significand = Math.floor(value / Math.pow(10, exp));\n\n\t\t\treturn significand * Math.pow(10, exp);\n\t\t},\n\t\tgetPixelForValue: function(value) {\n\t\t\tvar me = this;\n\t\t\tvar reverse = me.options.ticks.reverse;\n\t\t\tvar log10 = helpers.log10;\n\t\t\tvar firstTickValue = me._getFirstTickValue(me.minNotZero);\n\t\t\tvar offset = 0;\n\t\t\tvar innerDimension, pixel, start, end, sign;\n\n\t\t\tvalue = +me.getRightValue(value);\n\t\t\tif (reverse) {\n\t\t\t\tstart = me.end;\n\t\t\t\tend = me.start;\n\t\t\t\tsign = -1;\n\t\t\t} else {\n\t\t\t\tstart = me.start;\n\t\t\t\tend = me.end;\n\t\t\t\tsign = 1;\n\t\t\t}\n\t\t\tif (me.isHorizontal()) {\n\t\t\t\tinnerDimension = me.width;\n\t\t\t\tpixel = reverse ? me.right : me.left;\n\t\t\t} else {\n\t\t\t\tinnerDimension = me.height;\n\t\t\t\tsign *= -1; // invert, since the upper-left corner of the canvas is at pixel (0, 0)\n\t\t\t\tpixel = reverse ? me.top : me.bottom;\n\t\t\t}\n\t\t\tif (value !== start) {\n\t\t\t\tif (start === 0) { // include zero tick\n\t\t\t\t\toffset = helpers.getValueOrDefault(\n\t\t\t\t\t\tme.options.ticks.fontSize,\n\t\t\t\t\t\tChart.defaults.global.defaultFontSize\n\t\t\t\t\t);\n\t\t\t\t\tinnerDimension -= offset;\n\t\t\t\t\tstart = firstTickValue;\n\t\t\t\t}\n\t\t\t\tif (value !== 0) {\n\t\t\t\t\toffset += innerDimension / (log10(end) - log10(start)) * (log10(value) - log10(start));\n\t\t\t\t}\n\t\t\t\tpixel += sign * offset;\n\t\t\t}\n\t\t\treturn pixel;\n\t\t},\n\t\tgetValueForPixel: function(pixel) {\n\t\t\tvar me = this;\n\t\t\tvar reverse = me.options.ticks.reverse;\n\t\t\tvar log10 = helpers.log10;\n\t\t\tvar firstTickValue = me._getFirstTickValue(me.minNotZero);\n\t\t\tvar innerDimension, start, end, value;\n\n\t\t\tif (reverse) {\n\t\t\t\tstart = me.end;\n\t\t\t\tend = me.start;\n\t\t\t} else {\n\t\t\t\tstart = me.start;\n\t\t\t\tend = me.end;\n\t\t\t}\n\t\t\tif (me.isHorizontal()) {\n\t\t\t\tinnerDimension = me.width;\n\t\t\t\tvalue = reverse ? me.right - pixel : pixel - me.left;\n\t\t\t} else {\n\t\t\t\tinnerDimension = me.height;\n\t\t\t\tvalue = reverse ? pixel - me.top : me.bottom - pixel;\n\t\t\t}\n\t\t\tif (value !== start) {\n\t\t\t\tif (start === 0) { // include zero tick\n\t\t\t\t\tvar offset = helpers.getValueOrDefault(\n\t\t\t\t\t\tme.options.ticks.fontSize,\n\t\t\t\t\t\tChart.defaults.global.defaultFontSize\n\t\t\t\t\t);\n\t\t\t\t\tvalue -= offset;\n\t\t\t\t\tinnerDimension -= offset;\n\t\t\t\t\tstart = firstTickValue;\n\t\t\t\t}\n\t\t\t\tvalue *= log10(end) - log10(start);\n\t\t\t\tvalue /= innerDimension;\n\t\t\t\tvalue = Math.pow(10, log10(start) + value);\n\t\t\t}\n\t\t\treturn value;\n\t\t}\n\t});\n\tChart.scaleService.registerScaleType('logarithmic', LogarithmicScale, defaultConfig);\n\n};\n\n},{\"34\":34,\"45\":45}],57:[function(require,module,exports){\n'use strict';\n\nvar defaults = require(25);\nvar helpers = require(45);\nvar Ticks = require(34);\n\nmodule.exports = function(Chart) {\n\n\tvar globalDefaults = defaults.global;\n\n\tvar defaultConfig = {\n\t\tdisplay: true,\n\n\t\t// Boolean - Whether to animate scaling the chart from the centre\n\t\tanimate: true,\n\t\tposition: 'chartArea',\n\n\t\tangleLines: {\n\t\t\tdisplay: true,\n\t\t\tcolor: 'rgba(0, 0, 0, 0.1)',\n\t\t\tlineWidth: 1\n\t\t},\n\n\t\tgridLines: {\n\t\t\tcircular: false\n\t\t},\n\n\t\t// label settings\n\t\tticks: {\n\t\t\t// Boolean - Show a backdrop to the scale label\n\t\t\tshowLabelBackdrop: true,\n\n\t\t\t// String - The colour of the label backdrop\n\t\t\tbackdropColor: 'rgba(255,255,255,0.75)',\n\n\t\t\t// Number - The backdrop padding above & below the label in pixels\n\t\t\tbackdropPaddingY: 2,\n\n\t\t\t// Number - The backdrop padding to the side of the label in pixels\n\t\t\tbackdropPaddingX: 2,\n\n\t\t\tcallback: Ticks.formatters.linear\n\t\t},\n\n\t\tpointLabels: {\n\t\t\t// Boolean - if true, show point labels\n\t\t\tdisplay: true,\n\n\t\t\t// Number - Point label font size in pixels\n\t\t\tfontSize: 10,\n\n\t\t\t// Function - Used to convert point labels\n\t\t\tcallback: function(label) {\n\t\t\t\treturn label;\n\t\t\t}\n\t\t}\n\t};\n\n\tfunction getValueCount(scale) {\n\t\tvar opts = scale.options;\n\t\treturn opts.angleLines.display || opts.pointLabels.display ? scale.chart.data.labels.length : 0;\n\t}\n\n\tfunction getPointLabelFontOptions(scale) {\n\t\tvar pointLabelOptions = scale.options.pointLabels;\n\t\tvar fontSize = helpers.valueOrDefault(pointLabelOptions.fontSize, globalDefaults.defaultFontSize);\n\t\tvar fontStyle = helpers.valueOrDefault(pointLabelOptions.fontStyle, globalDefaults.defaultFontStyle);\n\t\tvar fontFamily = helpers.valueOrDefault(pointLabelOptions.fontFamily, globalDefaults.defaultFontFamily);\n\t\tvar font = helpers.fontString(fontSize, fontStyle, fontFamily);\n\n\t\treturn {\n\t\t\tsize: fontSize,\n\t\t\tstyle: fontStyle,\n\t\t\tfamily: fontFamily,\n\t\t\tfont: font\n\t\t};\n\t}\n\n\tfunction measureLabelSize(ctx, fontSize, label) {\n\t\tif (helpers.isArray(label)) {\n\t\t\treturn {\n\t\t\t\tw: helpers.longestText(ctx, ctx.font, label),\n\t\t\t\th: (label.length * fontSize) + ((label.length - 1) * 1.5 * fontSize)\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tw: ctx.measureText(label).width,\n\t\t\th: fontSize\n\t\t};\n\t}\n\n\tfunction determineLimits(angle, pos, size, min, max) {\n\t\tif (angle === min || angle === max) {\n\t\t\treturn {\n\t\t\t\tstart: pos - (size / 2),\n\t\t\t\tend: pos + (size / 2)\n\t\t\t};\n\t\t} else if (angle < min || angle > max) {\n\t\t\treturn {\n\t\t\t\tstart: pos - size - 5,\n\t\t\t\tend: pos\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tstart: pos,\n\t\t\tend: pos + size + 5\n\t\t};\n\t}\n\n\t/**\n\t * Helper function to fit a radial linear scale with point labels\n\t */\n\tfunction fitWithPointLabels(scale) {\n\t\t/*\n\t\t * Right, this is really confusing and there is a lot of maths going on here\n\t\t * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9\n\t\t *\n\t\t * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif\n\t\t *\n\t\t * Solution:\n\t\t *\n\t\t * We assume the radius of the polygon is half the size of the canvas at first\n\t\t * at each index we check if the text overlaps.\n\t\t *\n\t\t * Where it does, we store that angle and that index.\n\t\t *\n\t\t * After finding the largest index and angle we calculate how much we need to remove\n\t\t * from the shape radius to move the point inwards by that x.\n\t\t *\n\t\t * We average the left and right distances to get the maximum shape radius that can fit in the box\n\t\t * along with labels.\n\t\t *\n\t\t * Once we have that, we can find the centre point for the chart, by taking the x text protrusion\n\t\t * on each side, removing that from the size, halving it and adding the left x protrusion width.\n\t\t *\n\t\t * This will mean we have a shape fitted to the canvas, as large as it can be with the labels\n\t\t * and position it in the most space efficient manner\n\t\t *\n\t\t * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif\n\t\t */\n\n\t\tvar plFont = getPointLabelFontOptions(scale);\n\n\t\t// Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.\n\t\t// Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points\n\t\tvar largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);\n\t\tvar furthestLimits = {\n\t\t\tr: scale.width,\n\t\t\tl: 0,\n\t\t\tt: scale.height,\n\t\t\tb: 0\n\t\t};\n\t\tvar furthestAngles = {};\n\t\tvar i, textSize, pointPosition;\n\n\t\tscale.ctx.font = plFont.font;\n\t\tscale._pointLabelSizes = [];\n\n\t\tvar valueCount = getValueCount(scale);\n\t\tfor (i = 0; i < valueCount; i++) {\n\t\t\tpointPosition = scale.getPointPosition(i, largestPossibleRadius);\n\t\t\ttextSize = measureLabelSize(scale.ctx, plFont.size, scale.pointLabels[i] || '');\n\t\t\tscale._pointLabelSizes[i] = textSize;\n\n\t\t\t// Add quarter circle to make degree 0 mean top of circle\n\t\t\tvar angleRadians = scale.getIndexAngle(i);\n\t\t\tvar angle = helpers.toDegrees(angleRadians) % 360;\n\t\t\tvar hLimits = determineLimits(angle, pointPosition.x, textSize.w, 0, 180);\n\t\t\tvar vLimits = determineLimits(angle, pointPosition.y, textSize.h, 90, 270);\n\n\t\t\tif (hLimits.start < furthestLimits.l) {\n\t\t\t\tfurthestLimits.l = hLimits.start;\n\t\t\t\tfurthestAngles.l = angleRadians;\n\t\t\t}\n\n\t\t\tif (hLimits.end > furthestLimits.r) {\n\t\t\t\tfurthestLimits.r = hLimits.end;\n\t\t\t\tfurthestAngles.r = angleRadians;\n\t\t\t}\n\n\t\t\tif (vLimits.start < furthestLimits.t) {\n\t\t\t\tfurthestLimits.t = vLimits.start;\n\t\t\t\tfurthestAngles.t = angleRadians;\n\t\t\t}\n\n\t\t\tif (vLimits.end > furthestLimits.b) {\n\t\t\t\tfurthestLimits.b = vLimits.end;\n\t\t\t\tfurthestAngles.b = angleRadians;\n\t\t\t}\n\t\t}\n\n\t\tscale.setReductions(largestPossibleRadius, furthestLimits, furthestAngles);\n\t}\n\n\t/**\n\t * Helper function to fit a radial linear scale with no point labels\n\t */\n\tfunction fit(scale) {\n\t\tvar largestPossibleRadius = Math.min(scale.height / 2, scale.width / 2);\n\t\tscale.drawingArea = Math.round(largestPossibleRadius);\n\t\tscale.setCenterPoint(0, 0, 0, 0);\n\t}\n\n\tfunction getTextAlignForAngle(angle) {\n\t\tif (angle === 0 || angle === 180) {\n\t\t\treturn 'center';\n\t\t} else if (angle < 180) {\n\t\t\treturn 'left';\n\t\t}\n\n\t\treturn 'right';\n\t}\n\n\tfunction fillText(ctx, text, position, fontSize) {\n\t\tif (helpers.isArray(text)) {\n\t\t\tvar y = position.y;\n\t\t\tvar spacing = 1.5 * fontSize;\n\n\t\t\tfor (var i = 0; i < text.length; ++i) {\n\t\t\t\tctx.fillText(text[i], position.x, y);\n\t\t\t\ty += spacing;\n\t\t\t}\n\t\t} else {\n\t\t\tctx.fillText(text, position.x, position.y);\n\t\t}\n\t}\n\n\tfunction adjustPointPositionForLabelHeight(angle, textSize, position) {\n\t\tif (angle === 90 || angle === 270) {\n\t\t\tposition.y -= (textSize.h / 2);\n\t\t} else if (angle > 270 || angle < 90) {\n\t\t\tposition.y -= textSize.h;\n\t\t}\n\t}\n\n\tfunction drawPointLabels(scale) {\n\t\tvar ctx = scale.ctx;\n\t\tvar opts = scale.options;\n\t\tvar angleLineOpts = opts.angleLines;\n\t\tvar pointLabelOpts = opts.pointLabels;\n\n\t\tctx.lineWidth = angleLineOpts.lineWidth;\n\t\tctx.strokeStyle = angleLineOpts.color;\n\n\t\tvar outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);\n\n\t\t// Point Label Font\n\t\tvar plFont = getPointLabelFontOptions(scale);\n\n\t\tctx.textBaseline = 'top';\n\n\t\tfor (var i = getValueCount(scale) - 1; i >= 0; i--) {\n\t\t\tif (angleLineOpts.display) {\n\t\t\t\tvar outerPosition = scale.getPointPosition(i, outerDistance);\n\t\t\t\tctx.beginPath();\n\t\t\t\tctx.moveTo(scale.xCenter, scale.yCenter);\n\t\t\t\tctx.lineTo(outerPosition.x, outerPosition.y);\n\t\t\t\tctx.stroke();\n\t\t\t\tctx.closePath();\n\t\t\t}\n\n\t\t\tif (pointLabelOpts.display) {\n\t\t\t\t// Extra 3px out for some label spacing\n\t\t\t\tvar pointLabelPosition = scale.getPointPosition(i, outerDistance + 5);\n\n\t\t\t\t// Keep this in loop since we may support array properties here\n\t\t\t\tvar pointLabelFontColor = helpers.valueAtIndexOrDefault(pointLabelOpts.fontColor, i, globalDefaults.defaultFontColor);\n\t\t\t\tctx.font = plFont.font;\n\t\t\t\tctx.fillStyle = pointLabelFontColor;\n\n\t\t\t\tvar angleRadians = scale.getIndexAngle(i);\n\t\t\t\tvar angle = helpers.toDegrees(angleRadians);\n\t\t\t\tctx.textAlign = getTextAlignForAngle(angle);\n\t\t\t\tadjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition);\n\t\t\t\tfillText(ctx, scale.pointLabels[i] || '', pointLabelPosition, plFont.size);\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction drawRadiusLine(scale, gridLineOpts, radius, index) {\n\t\tvar ctx = scale.ctx;\n\t\tctx.strokeStyle = helpers.valueAtIndexOrDefault(gridLineOpts.color, index - 1);\n\t\tctx.lineWidth = helpers.valueAtIndexOrDefault(gridLineOpts.lineWidth, index - 1);\n\n\t\tif (scale.options.gridLines.circular) {\n\t\t\t// Draw circular arcs between the points\n\t\t\tctx.beginPath();\n\t\t\tctx.arc(scale.xCenter, scale.yCenter, radius, 0, Math.PI * 2);\n\t\t\tctx.closePath();\n\t\t\tctx.stroke();\n\t\t} else {\n\t\t\t// Draw straight lines connecting each index\n\t\t\tvar valueCount = getValueCount(scale);\n\n\t\t\tif (valueCount === 0) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tctx.beginPath();\n\t\t\tvar pointPosition = scale.getPointPosition(0, radius);\n\t\t\tctx.moveTo(pointPosition.x, pointPosition.y);\n\n\t\t\tfor (var i = 1; i < valueCount; i++) {\n\t\t\t\tpointPosition = scale.getPointPosition(i, radius);\n\t\t\t\tctx.lineTo(pointPosition.x, pointPosition.y);\n\t\t\t}\n\n\t\t\tctx.closePath();\n\t\t\tctx.stroke();\n\t\t}\n\t}\n\n\tfunction numberOrZero(param) {\n\t\treturn helpers.isNumber(param) ? param : 0;\n\t}\n\n\tvar LinearRadialScale = Chart.LinearScaleBase.extend({\n\t\tsetDimensions: function() {\n\t\t\tvar me = this;\n\t\t\tvar opts = me.options;\n\t\t\tvar tickOpts = opts.ticks;\n\t\t\t// Set the unconstrained dimension before label rotation\n\t\t\tme.width = me.maxWidth;\n\t\t\tme.height = me.maxHeight;\n\t\t\tme.xCenter = Math.round(me.width / 2);\n\t\t\tme.yCenter = Math.round(me.height / 2);\n\n\t\t\tvar minSize = helpers.min([me.height, me.width]);\n\t\t\tvar tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);\n\t\t\tme.drawingArea = opts.display ? (minSize / 2) - (tickFontSize / 2 + tickOpts.backdropPaddingY) : (minSize / 2);\n\t\t},\n\t\tdetermineDataLimits: function() {\n\t\t\tvar me = this;\n\t\t\tvar chart = me.chart;\n\t\t\tvar min = Number.POSITIVE_INFINITY;\n\t\t\tvar max = Number.NEGATIVE_INFINITY;\n\n\t\t\thelpers.each(chart.data.datasets, function(dataset, datasetIndex) {\n\t\t\t\tif (chart.isDatasetVisible(datasetIndex)) {\n\t\t\t\t\tvar meta = chart.getDatasetMeta(datasetIndex);\n\n\t\t\t\t\thelpers.each(dataset.data, function(rawValue, index) {\n\t\t\t\t\t\tvar value = +me.getRightValue(rawValue);\n\t\t\t\t\t\tif (isNaN(value) || meta.data[index].hidden) {\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tmin = Math.min(value, min);\n\t\t\t\t\t\tmax = Math.max(value, max);\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tme.min = (min === Number.POSITIVE_INFINITY ? 0 : min);\n\t\t\tme.max = (max === Number.NEGATIVE_INFINITY ? 0 : max);\n\n\t\t\t// Common base implementation to handle ticks.min, ticks.max, ticks.beginAtZero\n\t\t\tme.handleTickRangeOptions();\n\t\t},\n\t\tgetTickLimit: function() {\n\t\t\tvar tickOpts = this.options.ticks;\n\t\t\tvar tickFontSize = helpers.valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);\n\t\t\treturn Math.min(tickOpts.maxTicksLimit ? tickOpts.maxTicksLimit : 11, Math.ceil(this.drawingArea / (1.5 * tickFontSize)));\n\t\t},\n\t\tconvertTicksToLabels: function() {\n\t\t\tvar me = this;\n\n\t\t\tChart.LinearScaleBase.prototype.convertTicksToLabels.call(me);\n\n\t\t\t// Point labels\n\t\t\tme.pointLabels = me.chart.data.labels.map(me.options.pointLabels.callback, me);\n\t\t},\n\t\tgetLabelForIndex: function(index, datasetIndex) {\n\t\t\treturn +this.getRightValue(this.chart.data.datasets[datasetIndex].data[index]);\n\t\t},\n\t\tfit: function() {\n\t\t\tif (this.options.pointLabels.display) {\n\t\t\t\tfitWithPointLabels(this);\n\t\t\t} else {\n\t\t\t\tfit(this);\n\t\t\t}\n\t\t},\n\t\t/**\n\t\t * Set radius reductions and determine new radius and center point\n\t\t * @private\n\t\t */\n\t\tsetReductions: function(largestPossibleRadius, furthestLimits, furthestAngles) {\n\t\t\tvar me = this;\n\t\t\tvar radiusReductionLeft = furthestLimits.l / Math.sin(furthestAngles.l);\n\t\t\tvar radiusReductionRight = Math.max(furthestLimits.r - me.width, 0) / Math.sin(furthestAngles.r);\n\t\t\tvar radiusReductionTop = -furthestLimits.t / Math.cos(furthestAngles.t);\n\t\t\tvar radiusReductionBottom = -Math.max(furthestLimits.b - me.height, 0) / Math.cos(furthestAngles.b);\n\n\t\t\tradiusReductionLeft = numberOrZero(radiusReductionLeft);\n\t\t\tradiusReductionRight = numberOrZero(radiusReductionRight);\n\t\t\tradiusReductionTop = numberOrZero(radiusReductionTop);\n\t\t\tradiusReductionBottom = numberOrZero(radiusReductionBottom);\n\n\t\t\tme.drawingArea = Math.min(\n\t\t\t\tMath.round(largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2),\n\t\t\t\tMath.round(largestPossibleRadius - (radiusReductionTop + radiusReductionBottom) / 2));\n\t\t\tme.setCenterPoint(radiusReductionLeft, radiusReductionRight, radiusReductionTop, radiusReductionBottom);\n\t\t},\n\t\tsetCenterPoint: function(leftMovement, rightMovement, topMovement, bottomMovement) {\n\t\t\tvar me = this;\n\t\t\tvar maxRight = me.width - rightMovement - me.drawingArea;\n\t\t\tvar maxLeft = leftMovement + me.drawingArea;\n\t\t\tvar maxTop = topMovement + me.drawingArea;\n\t\t\tvar maxBottom = me.height - bottomMovement - me.drawingArea;\n\n\t\t\tme.xCenter = Math.round(((maxLeft + maxRight) / 2) + me.left);\n\t\t\tme.yCenter = Math.round(((maxTop + maxBottom) / 2) + me.top);\n\t\t},\n\n\t\tgetIndexAngle: function(index) {\n\t\t\tvar angleMultiplier = (Math.PI * 2) / getValueCount(this);\n\t\t\tvar startAngle = this.chart.options && this.chart.options.startAngle ?\n\t\t\t\tthis.chart.options.startAngle :\n\t\t\t\t0;\n\n\t\t\tvar startAngleRadians = startAngle * Math.PI * 2 / 360;\n\n\t\t\t// Start from the top instead of right, so remove a quarter of the circle\n\t\t\treturn index * angleMultiplier + startAngleRadians;\n\t\t},\n\t\tgetDistanceFromCenterForValue: function(value) {\n\t\t\tvar me = this;\n\n\t\t\tif (value === null) {\n\t\t\t\treturn 0; // null always in center\n\t\t\t}\n\n\t\t\t// Take into account half font size + the yPadding of the top value\n\t\t\tvar scalingFactor = me.drawingArea / (me.max - me.min);\n\t\t\tif (me.options.ticks.reverse) {\n\t\t\t\treturn (me.max - value) * scalingFactor;\n\t\t\t}\n\t\t\treturn (value - me.min) * scalingFactor;\n\t\t},\n\t\tgetPointPosition: function(index, distanceFromCenter) {\n\t\t\tvar me = this;\n\t\t\tvar thisAngle = me.getIndexAngle(index) - (Math.PI / 2);\n\t\t\treturn {\n\t\t\t\tx: Math.round(Math.cos(thisAngle) * distanceFromCenter) + me.xCenter,\n\t\t\t\ty: Math.round(Math.sin(thisAngle) * distanceFromCenter) + me.yCenter\n\t\t\t};\n\t\t},\n\t\tgetPointPositionForValue: function(index, value) {\n\t\t\treturn this.getPointPosition(index, this.getDistanceFromCenterForValue(value));\n\t\t},\n\n\t\tgetBasePosition: function() {\n\t\t\tvar me = this;\n\t\t\tvar min = me.min;\n\t\t\tvar max = me.max;\n\n\t\t\treturn me.getPointPositionForValue(0,\n\t\t\t\tme.beginAtZero ? 0 :\n\t\t\t\tmin < 0 && max < 0 ? max :\n\t\t\t\tmin > 0 && max > 0 ? min :\n\t\t\t\t0);\n\t\t},\n\n\t\tdraw: function() {\n\t\t\tvar me = this;\n\t\t\tvar opts = me.options;\n\t\t\tvar gridLineOpts = opts.gridLines;\n\t\t\tvar tickOpts = opts.ticks;\n\t\t\tvar valueOrDefault = helpers.valueOrDefault;\n\n\t\t\tif (opts.display) {\n\t\t\t\tvar ctx = me.ctx;\n\t\t\t\tvar startAngle = this.getIndexAngle(0);\n\n\t\t\t\t// Tick Font\n\t\t\t\tvar tickFontSize = valueOrDefault(tickOpts.fontSize, globalDefaults.defaultFontSize);\n\t\t\t\tvar tickFontStyle = valueOrDefault(tickOpts.fontStyle, globalDefaults.defaultFontStyle);\n\t\t\t\tvar tickFontFamily = valueOrDefault(tickOpts.fontFamily, globalDefaults.defaultFontFamily);\n\t\t\t\tvar tickLabelFont = helpers.fontString(tickFontSize, tickFontStyle, tickFontFamily);\n\n\t\t\t\thelpers.each(me.ticks, function(label, index) {\n\t\t\t\t\t// Don't draw a centre value (if it is minimum)\n\t\t\t\t\tif (index > 0 || tickOpts.reverse) {\n\t\t\t\t\t\tvar yCenterOffset = me.getDistanceFromCenterForValue(me.ticksAsNumbers[index]);\n\n\t\t\t\t\t\t// Draw circular lines around the scale\n\t\t\t\t\t\tif (gridLineOpts.display && index !== 0) {\n\t\t\t\t\t\t\tdrawRadiusLine(me, gridLineOpts, yCenterOffset, index);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (tickOpts.display) {\n\t\t\t\t\t\t\tvar tickFontColor = valueOrDefault(tickOpts.fontColor, globalDefaults.defaultFontColor);\n\t\t\t\t\t\t\tctx.font = tickLabelFont;\n\n\t\t\t\t\t\t\tctx.save();\n\t\t\t\t\t\t\tctx.translate(me.xCenter, me.yCenter);\n\t\t\t\t\t\t\tctx.rotate(startAngle);\n\n\t\t\t\t\t\t\tif (tickOpts.showLabelBackdrop) {\n\t\t\t\t\t\t\t\tvar labelWidth = ctx.measureText(label).width;\n\t\t\t\t\t\t\t\tctx.fillStyle = tickOpts.backdropColor;\n\t\t\t\t\t\t\t\tctx.fillRect(\n\t\t\t\t\t\t\t\t\t-labelWidth / 2 - tickOpts.backdropPaddingX,\n\t\t\t\t\t\t\t\t\t-yCenterOffset - tickFontSize / 2 - tickOpts.backdropPaddingY,\n\t\t\t\t\t\t\t\t\tlabelWidth + tickOpts.backdropPaddingX * 2,\n\t\t\t\t\t\t\t\t\ttickFontSize + tickOpts.backdropPaddingY * 2\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tctx.textAlign = 'center';\n\t\t\t\t\t\t\tctx.textBaseline = 'middle';\n\t\t\t\t\t\t\tctx.fillStyle = tickFontColor;\n\t\t\t\t\t\t\tctx.fillText(label, 0, -yCenterOffset);\n\t\t\t\t\t\t\tctx.restore();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tif (opts.angleLines.display || opts.pointLabels.display) {\n\t\t\t\t\tdrawPointLabels(me);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t});\n\tChart.scaleService.registerScaleType('radialLinear', LinearRadialScale, defaultConfig);\n\n};\n\n},{\"25\":25,\"34\":34,\"45\":45}],58:[function(require,module,exports){\n/* global window: false */\n'use strict';\n\nvar moment = require(1);\nmoment = typeof moment === 'function' ? moment : window.moment;\n\nvar defaults = require(25);\nvar helpers = require(45);\n\n// Integer constants are from the ES6 spec.\nvar MIN_INTEGER = Number.MIN_SAFE_INTEGER || -9007199254740991;\nvar MAX_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;\n\nvar INTERVALS = {\n\tmillisecond: {\n\t\tcommon: true,\n\t\tsize: 1,\n\t\tsteps: [1, 2, 5, 10, 20, 50, 100, 250, 500]\n\t},\n\tsecond: {\n\t\tcommon: true,\n\t\tsize: 1000,\n\t\tsteps: [1, 2, 5, 10, 30]\n\t},\n\tminute: {\n\t\tcommon: true,\n\t\tsize: 60000,\n\t\tsteps: [1, 2, 5, 10, 30]\n\t},\n\thour: {\n\t\tcommon: true,\n\t\tsize: 3600000,\n\t\tsteps: [1, 2, 3, 6, 12]\n\t},\n\tday: {\n\t\tcommon: true,\n\t\tsize: 86400000,\n\t\tsteps: [1, 2, 5]\n\t},\n\tweek: {\n\t\tcommon: false,\n\t\tsize: 604800000,\n\t\tsteps: [1, 2, 3, 4]\n\t},\n\tmonth: {\n\t\tcommon: true,\n\t\tsize: 2.628e9,\n\t\tsteps: [1, 2, 3]\n\t},\n\tquarter: {\n\t\tcommon: false,\n\t\tsize: 7.884e9,\n\t\tsteps: [1, 2, 3, 4]\n\t},\n\tyear: {\n\t\tcommon: true,\n\t\tsize: 3.154e10\n\t}\n};\n\nvar UNITS = Object.keys(INTERVALS);\n\nfunction sorter(a, b) {\n\treturn a - b;\n}\n\nfunction arrayUnique(items) {\n\tvar hash = {};\n\tvar out = [];\n\tvar i, ilen, item;\n\n\tfor (i = 0, ilen = items.length; i < ilen; ++i) {\n\t\titem = items[i];\n\t\tif (!hash[item]) {\n\t\t\thash[item] = true;\n\t\t\tout.push(item);\n\t\t}\n\t}\n\n\treturn out;\n}\n\n/**\n * Returns an array of {time, pos} objects used to interpolate a specific `time` or position\n * (`pos`) on the scale, by searching entries before and after the requested value. `pos` is\n * a decimal between 0 and 1: 0 being the start of the scale (left or top) and 1 the other\n * extremity (left + width or top + height). Note that it would be more optimized to directly\n * store pre-computed pixels, but the scale dimensions are not guaranteed at the time we need\n * to create the lookup table. The table ALWAYS contains at least two items: min and max.\n *\n * @param {Number[]} timestamps - timestamps sorted from lowest to highest.\n * @param {String} distribution - If 'linear', timestamps will be spread linearly along the min\n * and max range, so basically, the table will contains only two items: {min, 0} and {max, 1}.\n * If 'series', timestamps will be positioned at the same distance from each other. In this\n * case, only timestamps that break the time linearity are registered, meaning that in the\n * best case, all timestamps are linear, the table contains only min and max.\n */\nfunction buildLookupTable(timestamps, min, max, distribution) {\n\tif (distribution === 'linear' || !timestamps.length) {\n\t\treturn [\n\t\t\t{time: min, pos: 0},\n\t\t\t{time: max, pos: 1}\n\t\t];\n\t}\n\n\tvar table = [];\n\tvar items = [min];\n\tvar i, ilen, prev, curr, next;\n\n\tfor (i = 0, ilen = timestamps.length; i < ilen; ++i) {\n\t\tcurr = timestamps[i];\n\t\tif (curr > min && curr < max) {\n\t\t\titems.push(curr);\n\t\t}\n\t}\n\n\titems.push(max);\n\n\tfor (i = 0, ilen = items.length; i < ilen; ++i) {\n\t\tnext = items[i + 1];\n\t\tprev = items[i - 1];\n\t\tcurr = items[i];\n\n\t\t// only add points that breaks the scale linearity\n\t\tif (prev === undefined || next === undefined || Math.round((next + prev) / 2) !== curr) {\n\t\t\ttable.push({time: curr, pos: i / (ilen - 1)});\n\t\t}\n\t}\n\n\treturn table;\n}\n\n// @see adapted from http://www.anujgakhar.com/2014/03/01/binary-search-in-javascript/\nfunction lookup(table, key, value) {\n\tvar lo = 0;\n\tvar hi = table.length - 1;\n\tvar mid, i0, i1;\n\n\twhile (lo >= 0 && lo <= hi) {\n\t\tmid = (lo + hi) >> 1;\n\t\ti0 = table[mid - 1] || null;\n\t\ti1 = table[mid];\n\n\t\tif (!i0) {\n\t\t\t// given value is outside table (before first item)\n\t\t\treturn {lo: null, hi: i1};\n\t\t} else if (i1[key] < value) {\n\t\t\tlo = mid + 1;\n\t\t} else if (i0[key] > value) {\n\t\t\thi = mid - 1;\n\t\t} else {\n\t\t\treturn {lo: i0, hi: i1};\n\t\t}\n\t}\n\n\t// given value is outside table (after last item)\n\treturn {lo: i1, hi: null};\n}\n\n/**\n * Linearly interpolates the given source `value` using the table items `skey` values and\n * returns the associated `tkey` value. For example, interpolate(table, 'time', 42, 'pos')\n * returns the position for a timestamp equal to 42. If value is out of bounds, values at\n * index [0, 1] or [n - 1, n] are used for the interpolation.\n */\nfunction interpolate(table, skey, sval, tkey) {\n\tvar range = lookup(table, skey, sval);\n\n\t// Note: the lookup table ALWAYS contains at least 2 items (min and max)\n\tvar prev = !range.lo ? table[0] : !range.hi ? table[table.length - 2] : range.lo;\n\tvar next = !range.lo ? table[1] : !range.hi ? table[table.length - 1] : range.hi;\n\n\tvar span = next[skey] - prev[skey];\n\tvar ratio = span ? (sval - prev[skey]) / span : 0;\n\tvar offset = (next[tkey] - prev[tkey]) * ratio;\n\n\treturn prev[tkey] + offset;\n}\n\n/**\n * Convert the given value to a moment object using the given time options.\n * @see http://momentjs.com/docs/#/parsing/\n */\nfunction momentify(value, options) {\n\tvar parser = options.parser;\n\tvar format = options.parser || options.format;\n\n\tif (typeof parser === 'function') {\n\t\treturn parser(value);\n\t}\n\n\tif (typeof value === 'string' && typeof format === 'string') {\n\t\treturn moment(value, format);\n\t}\n\n\tif (!(value instanceof moment)) {\n\t\tvalue = moment(value);\n\t}\n\n\tif (value.isValid()) {\n\t\treturn value;\n\t}\n\n\t// Labels are in an incompatible moment format and no `parser` has been provided.\n\t// The user might still use the deprecated `format` option to convert his inputs.\n\tif (typeof format === 'function') {\n\t\treturn format(value);\n\t}\n\n\treturn value;\n}\n\nfunction parse(input, scale) {\n\tif (helpers.isNullOrUndef(input)) {\n\t\treturn null;\n\t}\n\n\tvar options = scale.options.time;\n\tvar value = momentify(scale.getRightValue(input), options);\n\tif (!value.isValid()) {\n\t\treturn null;\n\t}\n\n\tif (options.round) {\n\t\tvalue.startOf(options.round);\n\t}\n\n\treturn value.valueOf();\n}\n\n/**\n * Returns the number of unit to skip to be able to display up to `capacity` number of ticks\n * in `unit` for the given `min` / `max` range and respecting the interval steps constraints.\n */\nfunction determineStepSize(min, max, unit, capacity) {\n\tvar range = max - min;\n\tvar interval = INTERVALS[unit];\n\tvar milliseconds = interval.size;\n\tvar steps = interval.steps;\n\tvar i, ilen, factor;\n\n\tif (!steps) {\n\t\treturn Math.ceil(range / (capacity * milliseconds));\n\t}\n\n\tfor (i = 0, ilen = steps.length; i < ilen; ++i) {\n\t\tfactor = steps[i];\n\t\tif (Math.ceil(range / (milliseconds * factor)) <= capacity) {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn factor;\n}\n\n/**\n * Figures out what unit results in an appropriate number of auto-generated ticks\n */\nfunction determineUnitForAutoTicks(minUnit, min, max, capacity) {\n\tvar ilen = UNITS.length;\n\tvar i, interval, factor;\n\n\tfor (i = UNITS.indexOf(minUnit); i < ilen - 1; ++i) {\n\t\tinterval = INTERVALS[UNITS[i]];\n\t\tfactor = interval.steps ? interval.steps[interval.steps.length - 1] : MAX_INTEGER;\n\n\t\tif (interval.common && Math.ceil((max - min) / (factor * interval.size)) <= capacity) {\n\t\t\treturn UNITS[i];\n\t\t}\n\t}\n\n\treturn UNITS[ilen - 1];\n}\n\n/**\n * Figures out what unit to format a set of ticks with\n */\nfunction determineUnitForFormatting(ticks, minUnit, min, max) {\n\tvar duration = moment.duration(moment(max).diff(moment(min)));\n\tvar ilen = UNITS.length;\n\tvar i, unit;\n\n\tfor (i = ilen - 1; i >= UNITS.indexOf(minUnit); i--) {\n\t\tunit = UNITS[i];\n\t\tif (INTERVALS[unit].common && duration.as(unit) >= ticks.length) {\n\t\t\treturn unit;\n\t\t}\n\t}\n\n\treturn UNITS[minUnit ? UNITS.indexOf(minUnit) : 0];\n}\n\nfunction determineMajorUnit(unit) {\n\tfor (var i = UNITS.indexOf(unit) + 1, ilen = UNITS.length; i < ilen; ++i) {\n\t\tif (INTERVALS[UNITS[i]].common) {\n\t\t\treturn UNITS[i];\n\t\t}\n\t}\n}\n\n/**\n * Generates a maximum of `capacity` timestamps between min and max, rounded to the\n * `minor` unit, aligned on the `major` unit and using the given scale time `options`.\n * Important: this method can return ticks outside the min and max range, it's the\n * responsibility of the calling code to clamp values if needed.\n */\nfunction generate(min, max, capacity, options) {\n\tvar timeOpts = options.time;\n\tvar minor = timeOpts.unit || determineUnitForAutoTicks(timeOpts.minUnit, min, max, capacity);\n\tvar major = determineMajorUnit(minor);\n\tvar stepSize = helpers.valueOrDefault(timeOpts.stepSize, timeOpts.unitStepSize);\n\tvar weekday = minor === 'week' ? timeOpts.isoWeekday : false;\n\tvar majorTicksEnabled = options.ticks.major.enabled;\n\tvar interval = INTERVALS[minor];\n\tvar first = moment(min);\n\tvar last = moment(max);\n\tvar ticks = [];\n\tvar time;\n\n\tif (!stepSize) {\n\t\tstepSize = determineStepSize(min, max, minor, capacity);\n\t}\n\n\t// For 'week' unit, handle the first day of week option\n\tif (weekday) {\n\t\tfirst = first.isoWeekday(weekday);\n\t\tlast = last.isoWeekday(weekday);\n\t}\n\n\t// Align first/last ticks on unit\n\tfirst = first.startOf(weekday ? 'day' : minor);\n\tlast = last.startOf(weekday ? 'day' : minor);\n\n\t// Make sure that the last tick include max\n\tif (last < max) {\n\t\tlast.add(1, minor);\n\t}\n\n\ttime = moment(first);\n\n\tif (majorTicksEnabled && major && !weekday && !timeOpts.round) {\n\t\t// Align the first tick on the previous `minor` unit aligned on the `major` unit:\n\t\t// we first aligned time on the previous `major` unit then add the number of full\n\t\t// stepSize there is between first and the previous major time.\n\t\ttime.startOf(major);\n\t\ttime.add(~~((first - time) / (interval.size * stepSize)) * stepSize, minor);\n\t}\n\n\tfor (; time < last; time.add(stepSize, minor)) {\n\t\tticks.push(+time);\n\t}\n\n\tticks.push(+time);\n\n\treturn ticks;\n}\n\n/**\n * Returns the right and left offsets from edges in the form of {left, right}.\n * Offsets are added when the `offset` option is true.\n */\nfunction computeOffsets(table, ticks, min, max, options) {\n\tvar left = 0;\n\tvar right = 0;\n\tvar upper, lower;\n\n\tif (options.offset && ticks.length) {\n\t\tif (!options.time.min) {\n\t\t\tupper = ticks.length > 1 ? ticks[1] : max;\n\t\t\tlower = ticks[0];\n\t\t\tleft = (\n\t\t\t\tinterpolate(table, 'time', upper, 'pos') -\n\t\t\t\tinterpolate(table, 'time', lower, 'pos')\n\t\t\t) / 2;\n\t\t}\n\t\tif (!options.time.max) {\n\t\t\tupper = ticks[ticks.length - 1];\n\t\t\tlower = ticks.length > 1 ? ticks[ticks.length - 2] : min;\n\t\t\tright = (\n\t\t\t\tinterpolate(table, 'time', upper, 'pos') -\n\t\t\t\tinterpolate(table, 'time', lower, 'pos')\n\t\t\t) / 2;\n\t\t}\n\t}\n\n\treturn {left: left, right: right};\n}\n\nfunction ticksFromTimestamps(values, majorUnit) {\n\tvar ticks = [];\n\tvar i, ilen, value, major;\n\n\tfor (i = 0, ilen = values.length; i < ilen; ++i) {\n\t\tvalue = values[i];\n\t\tmajor = majorUnit ? value === +moment(value).startOf(majorUnit) : false;\n\n\t\tticks.push({\n\t\t\tvalue: value,\n\t\t\tmajor: major\n\t\t});\n\t}\n\n\treturn ticks;\n}\n\nfunction determineLabelFormat(data, timeOpts) {\n\tvar i, momentDate, hasTime;\n\tvar ilen = data.length;\n\n\t// find the label with the most parts (milliseconds, minutes, etc.)\n\t// format all labels with the same level of detail as the most specific label\n\tfor (i = 0; i < ilen; i++) {\n\t\tmomentDate = momentify(data[i], timeOpts);\n\t\tif (momentDate.millisecond() !== 0) {\n\t\t\treturn 'MMM D, YYYY h:mm:ss.SSS a';\n\t\t}\n\t\tif (momentDate.second() !== 0 || momentDate.minute() !== 0 || momentDate.hour() !== 0) {\n\t\t\thasTime = true;\n\t\t}\n\t}\n\tif (hasTime) {\n\t\treturn 'MMM D, YYYY h:mm:ss a';\n\t}\n\treturn 'MMM D, YYYY';\n}\n\nmodule.exports = function(Chart) {\n\n\tvar defaultConfig = {\n\t\tposition: 'bottom',\n\n\t\t/**\n\t\t * Data distribution along the scale:\n\t\t * - 'linear': data are spread according to their time (distances can vary),\n\t\t * - 'series': data are spread at the same distance from each other.\n\t\t * @see https://github.com/chartjs/Chart.js/pull/4507\n\t\t * @since 2.7.0\n\t\t */\n\t\tdistribution: 'linear',\n\n\t\t/**\n\t\t * Scale boundary strategy (bypassed by min/max time options)\n\t\t * - `data`: make sure data are fully visible, ticks outside are removed\n\t\t * - `ticks`: make sure ticks are fully visible, data outside are truncated\n\t\t * @see https://github.com/chartjs/Chart.js/pull/4556\n\t\t * @since 2.7.0\n\t\t */\n\t\tbounds: 'data',\n\n\t\ttime: {\n\t\t\tparser: false, // false == a pattern string from http://momentjs.com/docs/#/parsing/string-format/ or a custom callback that converts its argument to a moment\n\t\t\tformat: false, // DEPRECATED false == date objects, moment object, callback or a pattern string from http://momentjs.com/docs/#/parsing/string-format/\n\t\t\tunit: false, // false == automatic or override with week, month, year, etc.\n\t\t\tround: false, // none, or override with week, month, year, etc.\n\t\t\tdisplayFormat: false, // DEPRECATED\n\t\t\tisoWeekday: false, // override week start day - see http://momentjs.com/docs/#/get-set/iso-weekday/\n\t\t\tminUnit: 'millisecond',\n\n\t\t\t// defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/\n\t\t\tdisplayFormats: {\n\t\t\t\tmillisecond: 'h:mm:ss.SSS a', // 11:20:01.123 AM,\n\t\t\t\tsecond: 'h:mm:ss a', // 11:20:01 AM\n\t\t\t\tminute: 'h:mm a', // 11:20 AM\n\t\t\t\thour: 'hA', // 5PM\n\t\t\t\tday: 'MMM D', // Sep 4\n\t\t\t\tweek: 'll', // Week 46, or maybe \"[W]WW - YYYY\" ?\n\t\t\t\tmonth: 'MMM YYYY', // Sept 2015\n\t\t\t\tquarter: '[Q]Q - YYYY', // Q3\n\t\t\t\tyear: 'YYYY' // 2015\n\t\t\t},\n\t\t},\n\t\tticks: {\n\t\t\tautoSkip: false,\n\n\t\t\t/**\n\t\t\t * Ticks generation input values:\n\t\t\t * - 'auto': generates \"optimal\" ticks based on scale size and time options.\n\t\t\t * - 'data': generates ticks from data (including labels from data {t|x|y} objects).\n\t\t\t * - 'labels': generates ticks from user given `data.labels` values ONLY.\n\t\t\t * @see https://github.com/chartjs/Chart.js/pull/4507\n\t\t\t * @since 2.7.0\n\t\t\t */\n\t\t\tsource: 'auto',\n\n\t\t\tmajor: {\n\t\t\t\tenabled: false\n\t\t\t}\n\t\t}\n\t};\n\n\tvar TimeScale = Chart.Scale.extend({\n\t\tinitialize: function() {\n\t\t\tif (!moment) {\n\t\t\t\tthrow new Error('Chart.js - Moment.js could not be found! You must include it before Chart.js to use the time scale. Download at https://momentjs.com');\n\t\t\t}\n\n\t\t\tthis.mergeTicksOptions();\n\n\t\t\tChart.Scale.prototype.initialize.call(this);\n\t\t},\n\n\t\tupdate: function() {\n\t\t\tvar me = this;\n\t\t\tvar options = me.options;\n\n\t\t\t// DEPRECATIONS: output a message only one time per update\n\t\t\tif (options.time && options.time.format) {\n\t\t\t\tconsole.warn('options.time.format is deprecated and replaced by options.time.parser.');\n\t\t\t}\n\n\t\t\treturn Chart.Scale.prototype.update.apply(me, arguments);\n\t\t},\n\n\t\t/**\n\t\t * Allows data to be referenced via 't' attribute\n\t\t */\n\t\tgetRightValue: function(rawValue) {\n\t\t\tif (rawValue && rawValue.t !== undefined) {\n\t\t\t\trawValue = rawValue.t;\n\t\t\t}\n\t\t\treturn Chart.Scale.prototype.getRightValue.call(this, rawValue);\n\t\t},\n\n\t\tdetermineDataLimits: function() {\n\t\t\tvar me = this;\n\t\t\tvar chart = me.chart;\n\t\t\tvar timeOpts = me.options.time;\n\t\t\tvar unit = timeOpts.unit || 'day';\n\t\t\tvar min = MAX_INTEGER;\n\t\t\tvar max = MIN_INTEGER;\n\t\t\tvar timestamps = [];\n\t\t\tvar datasets = [];\n\t\t\tvar labels = [];\n\t\t\tvar i, j, ilen, jlen, data, timestamp;\n\n\t\t\t// Convert labels to timestamps\n\t\t\tfor (i = 0, ilen = chart.data.labels.length; i < ilen; ++i) {\n\t\t\t\tlabels.push(parse(chart.data.labels[i], me));\n\t\t\t}\n\n\t\t\t// Convert data to timestamps\n\t\t\tfor (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {\n\t\t\t\tif (chart.isDatasetVisible(i)) {\n\t\t\t\t\tdata = chart.data.datasets[i].data;\n\n\t\t\t\t\t// Let's consider that all data have the same format.\n\t\t\t\t\tif (helpers.isObject(data[0])) {\n\t\t\t\t\t\tdatasets[i] = [];\n\n\t\t\t\t\t\tfor (j = 0, jlen = data.length; j < jlen; ++j) {\n\t\t\t\t\t\t\ttimestamp = parse(data[j], me);\n\t\t\t\t\t\t\ttimestamps.push(timestamp);\n\t\t\t\t\t\t\tdatasets[i][j] = timestamp;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\ttimestamps.push.apply(timestamps, labels);\n\t\t\t\t\t\tdatasets[i] = labels.slice(0);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tdatasets[i] = [];\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (labels.length) {\n\t\t\t\t// Sort labels **after** data have been converted\n\t\t\t\tlabels = arrayUnique(labels).sort(sorter);\n\t\t\t\tmin = Math.min(min, labels[0]);\n\t\t\t\tmax = Math.max(max, labels[labels.length - 1]);\n\t\t\t}\n\n\t\t\tif (timestamps.length) {\n\t\t\t\ttimestamps = arrayUnique(timestamps).sort(sorter);\n\t\t\t\tmin = Math.min(min, timestamps[0]);\n\t\t\t\tmax = Math.max(max, timestamps[timestamps.length - 1]);\n\t\t\t}\n\n\t\t\tmin = parse(timeOpts.min, me) || min;\n\t\t\tmax = parse(timeOpts.max, me) || max;\n\n\t\t\t// In case there is no valid min/max, set limits based on unit time option\n\t\t\tmin = min === MAX_INTEGER ? +moment().startOf(unit) : min;\n\t\t\tmax = max === MIN_INTEGER ? +moment().endOf(unit) + 1 : max;\n\n\t\t\t// Make sure that max is strictly higher than min (required by the lookup table)\n\t\t\tme.min = Math.min(min, max);\n\t\t\tme.max = Math.max(min + 1, max);\n\n\t\t\t// PRIVATE\n\t\t\tme._horizontal = me.isHorizontal();\n\t\t\tme._table = [];\n\t\t\tme._timestamps = {\n\t\t\t\tdata: timestamps,\n\t\t\t\tdatasets: datasets,\n\t\t\t\tlabels: labels\n\t\t\t};\n\t\t},\n\n\t\tbuildTicks: function() {\n\t\t\tvar me = this;\n\t\t\tvar min = me.min;\n\t\t\tvar max = me.max;\n\t\t\tvar options = me.options;\n\t\t\tvar timeOpts = options.time;\n\t\t\tvar timestamps = [];\n\t\t\tvar ticks = [];\n\t\t\tvar i, ilen, timestamp;\n\n\t\t\tswitch (options.ticks.source) {\n\t\t\tcase 'data':\n\t\t\t\ttimestamps = me._timestamps.data;\n\t\t\t\tbreak;\n\t\t\tcase 'labels':\n\t\t\t\ttimestamps = me._timestamps.labels;\n\t\t\t\tbreak;\n\t\t\tcase 'auto':\n\t\t\tdefault:\n\t\t\t\ttimestamps = generate(min, max, me.getLabelCapacity(min), options);\n\t\t\t}\n\n\t\t\tif (options.bounds === 'ticks' && timestamps.length) {\n\t\t\t\tmin = timestamps[0];\n\t\t\t\tmax = timestamps[timestamps.length - 1];\n\t\t\t}\n\n\t\t\t// Enforce limits with user min/max options\n\t\t\tmin = parse(timeOpts.min, me) || min;\n\t\t\tmax = parse(timeOpts.max, me) || max;\n\n\t\t\t// Remove ticks outside the min/max range\n\t\t\tfor (i = 0, ilen = timestamps.length; i < ilen; ++i) {\n\t\t\t\ttimestamp = timestamps[i];\n\t\t\t\tif (timestamp >= min && timestamp <= max) {\n\t\t\t\t\tticks.push(timestamp);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tme.min = min;\n\t\t\tme.max = max;\n\n\t\t\t// PRIVATE\n\t\t\tme._unit = timeOpts.unit || determineUnitForFormatting(ticks, timeOpts.minUnit, me.min, me.max);\n\t\t\tme._majorUnit = determineMajorUnit(me._unit);\n\t\t\tme._table = buildLookupTable(me._timestamps.data, min, max, options.distribution);\n\t\t\tme._offsets = computeOffsets(me._table, ticks, min, max, options);\n\t\t\tme._labelFormat = determineLabelFormat(me._timestamps.data, timeOpts);\n\n\t\t\treturn ticksFromTimestamps(ticks, me._majorUnit);\n\t\t},\n\n\t\tgetLabelForIndex: function(index, datasetIndex) {\n\t\t\tvar me = this;\n\t\t\tvar data = me.chart.data;\n\t\t\tvar timeOpts = me.options.time;\n\t\t\tvar label = data.labels && index < data.labels.length ? data.labels[index] : '';\n\t\t\tvar value = data.datasets[datasetIndex].data[index];\n\n\t\t\tif (helpers.isObject(value)) {\n\t\t\t\tlabel = me.getRightValue(value);\n\t\t\t}\n\t\t\tif (timeOpts.tooltipFormat) {\n\t\t\t\treturn momentify(label, timeOpts).format(timeOpts.tooltipFormat);\n\t\t\t}\n\t\t\tif (typeof label === 'string') {\n\t\t\t\treturn label;\n\t\t\t}\n\n\t\t\treturn momentify(label, timeOpts).format(me._labelFormat);\n\t\t},\n\n\t\t/**\n\t\t * Function to format an individual tick mark\n\t\t * @private\n\t\t */\n\t\ttickFormatFunction: function(tick, index, ticks, formatOverride) {\n\t\t\tvar me = this;\n\t\t\tvar options = me.options;\n\t\t\tvar time = tick.valueOf();\n\t\t\tvar formats = options.time.displayFormats;\n\t\t\tvar minorFormat = formats[me._unit];\n\t\t\tvar majorUnit = me._majorUnit;\n\t\t\tvar majorFormat = formats[majorUnit];\n\t\t\tvar majorTime = tick.clone().startOf(majorUnit).valueOf();\n\t\t\tvar majorTickOpts = options.ticks.major;\n\t\t\tvar major = majorTickOpts.enabled && majorUnit && majorFormat && time === majorTime;\n\t\t\tvar label = tick.format(formatOverride ? formatOverride : major ? majorFormat : minorFormat);\n\t\t\tvar tickOpts = major ? majorTickOpts : options.ticks.minor;\n\t\t\tvar formatter = helpers.valueOrDefault(tickOpts.callback, tickOpts.userCallback);\n\n\t\t\treturn formatter ? formatter(label, index, ticks) : label;\n\t\t},\n\n\t\tconvertTicksToLabels: function(ticks) {\n\t\t\tvar labels = [];\n\t\t\tvar i, ilen;\n\n\t\t\tfor (i = 0, ilen = ticks.length; i < ilen; ++i) {\n\t\t\t\tlabels.push(this.tickFormatFunction(moment(ticks[i].value), i, ticks));\n\t\t\t}\n\n\t\t\treturn labels;\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tgetPixelForOffset: function(time) {\n\t\t\tvar me = this;\n\t\t\tvar size = me._horizontal ? me.width : me.height;\n\t\t\tvar start = me._horizontal ? me.left : me.top;\n\t\t\tvar pos = interpolate(me._table, 'time', time, 'pos');\n\n\t\t\treturn start + size * (me._offsets.left + pos) / (me._offsets.left + 1 + me._offsets.right);\n\t\t},\n\n\t\tgetPixelForValue: function(value, index, datasetIndex) {\n\t\t\tvar me = this;\n\t\t\tvar time = null;\n\n\t\t\tif (index !== undefined && datasetIndex !== undefined) {\n\t\t\t\ttime = me._timestamps.datasets[datasetIndex][index];\n\t\t\t}\n\n\t\t\tif (time === null) {\n\t\t\t\ttime = parse(value, me);\n\t\t\t}\n\n\t\t\tif (time !== null) {\n\t\t\t\treturn me.getPixelForOffset(time);\n\t\t\t}\n\t\t},\n\n\t\tgetPixelForTick: function(index) {\n\t\t\tvar ticks = this.getTicks();\n\t\t\treturn index >= 0 && index < ticks.length ?\n\t\t\t\tthis.getPixelForOffset(ticks[index].value) :\n\t\t\t\tnull;\n\t\t},\n\n\t\tgetValueForPixel: function(pixel) {\n\t\t\tvar me = this;\n\t\t\tvar size = me._horizontal ? me.width : me.height;\n\t\t\tvar start = me._horizontal ? me.left : me.top;\n\t\t\tvar pos = (size ? (pixel - start) / size : 0) * (me._offsets.left + 1 + me._offsets.left) - me._offsets.right;\n\t\t\tvar time = interpolate(me._table, 'pos', pos, 'time');\n\n\t\t\treturn moment(time);\n\t\t},\n\n\t\t/**\n\t\t * Crude approximation of what the label width might be\n\t\t * @private\n\t\t */\n\t\tgetLabelWidth: function(label) {\n\t\t\tvar me = this;\n\t\t\tvar ticksOpts = me.options.ticks;\n\t\t\tvar tickLabelWidth = me.ctx.measureText(label).width;\n\t\t\tvar angle = helpers.toRadians(ticksOpts.maxRotation);\n\t\t\tvar cosRotation = Math.cos(angle);\n\t\t\tvar sinRotation = Math.sin(angle);\n\t\t\tvar tickFontSize = helpers.valueOrDefault(ticksOpts.fontSize, defaults.global.defaultFontSize);\n\n\t\t\treturn (tickLabelWidth * cosRotation) + (tickFontSize * sinRotation);\n\t\t},\n\n\t\t/**\n\t\t * @private\n\t\t */\n\t\tgetLabelCapacity: function(exampleTime) {\n\t\t\tvar me = this;\n\n\t\t\tvar formatOverride = me.options.time.displayFormats.millisecond;\t// Pick the longest format for guestimation\n\n\t\t\tvar exampleLabel = me.tickFormatFunction(moment(exampleTime), 0, [], formatOverride);\n\t\t\tvar tickLabelWidth = me.getLabelWidth(exampleLabel);\n\t\t\tvar innerWidth = me.isHorizontal() ? me.width : me.height;\n\n\t\t\tvar capacity = Math.floor(innerWidth / tickLabelWidth);\n\t\t\treturn capacity > 0 ? capacity : 1;\n\t\t}\n\t});\n\n\tChart.scaleService.registerScaleType('time', TimeScale, defaultConfig);\n};\n\n},{\"1\":1,\"25\":25,\"45\":45}]},{},[7])(7)\n});"
  },
  {
    "path": "bukuserver/static/bukuserver/js/bookmark.js",
    "content": "$(document).ready(function() {\n  window._tags = (Date.now() - (window._tagsQueried||0) < 1000 ? _tags :\n                  new Promise(resolve => {\n                    window._tagsQueried = Date.now();\n                    $.getJSON('/api/tags', ({tags}) => resolve(tags));\n                  }));\n  _tags.then(tags => $('input#tags').select2({tags, tokenSeparators: [',']}));\n});\n"
  },
  {
    "path": "bukuserver/static/bukuserver/js/buku_filter.js",
    "content": "$(document).ready(function () {  // synchronizing buku filters\n  let bukuFilters = () => $(`option[value^=\"buku_\"]`).parent(`.filter-op`);\n  let filterInput = filter => $(`.filter-val`, $(filter).parents('tr').first());\n  let adder = $(`.field-filters .filter`).filter(function () {return this.innerText === 'buku'}).get(0);\n  let sync = (key, $filter=bukuFilters().last(), value=filterInput($filter).val()) => {\n    ($filter.val() != key) && $filter.val(key).triggerHandler('change');\n    filterInput($filter).val(value).trigger('focus').on('change', evt => {value = evt.target.value});\n    $filter.on('change', (evt, param) => (param == '$norecur$') || bukuFilters().each(function () {\n      if (this == evt.target) {\n        filterInput(this).val(value);  // retaining the last filter value\n      } else {\n        let _value = filterInput(this).val();\n        $(this).val(evt.val).triggerHandler('change', '$norecur$');\n        filterInput(this).val(_value);  // retaining the last value for other filters\n      }\n    }));\n  };\n  bukuFilters().each(function () {sync(this.value, $(this))});\n  adder.onclick = () => {\n    try {\n      let key = bukuFilters().first().val() || 'buku_search_markers_match_all';\n      setTimeout(() => sync(key));\n    } catch (e) {  // ensuring the handler always returns false\n      console.error(e);\n    }\n    return false;\n  };\n});\n"
  },
  {
    "path": "bukuserver/static/bukuserver/js/filters_fix.js",
    "content": "$(document).ready(function () {\n  const IDX = [[36, 'a'], [10, 'A'], [0, '0']];\n  let idxChar = (i, [x, c]=IDX.find(([x]) => i >= x)) => String.fromCharCode(c.charCodeAt(0) + (i-x));\n  filter_form.onsubmit = function () {\n    $(`.filter-val[name]`, this).each((i, e) => {e.name = e.name.replace(/(?<=^flt)[^_]*(?=_)/, idxChar(i))});\n  };\n  $(`.pagination a:not([href^=javascript])`).each((_, e) => {\n    let url = new URL(e.href), params = Array.from(new URLSearchParams(url.search)), idx = 0;\n    params.forEach(kv => {\n      let m = kv[0].match(/^flt[^_]*(_.*)$/);\n      if (m) kv[0] = `flt${idxChar(idx++)}${m[1]}`;\n    });\n    e.href = Object.assign(url, {search: new URLSearchParams(params)});\n  });\n});\n"
  },
  {
    "path": "bukuserver/static/bukuserver/js/last_page.js",
    "content": "$(document).ready(function() {\n  $(`.pagination :contains(\"»\") a`).not(`[href^=\"javascript:\"]`).attr('href', (idx, href) =>\n    href.replace(/\\/?(\\?|$)/, '/last-page$1').replace(/([?&])page=[0-9]+(&|$)/, '$1'));\n});\n"
  },
  {
    "path": "bukuserver/static/bukuserver/js/order_filter.js",
    "content": "$(document).ready(function () {  // retaining state of same-kind filters on switch\n  let config = JSON.parse(document.getElementById('filter-groups-data').innerText);\n  let isOrder = k => config[k]?.[0].arg.startsWith('order_');\n  let orderFilters = () => $(`option[value^=\"order_\"]`).parent(`.filter-op`);\n  let filterInput = filter => $(`:is(input, select).filter-val`, $(filter).parents('tr').first());\n  let adder = $(`.field-filters .filter`).filter(function () {return isOrder(this.innerText)}).get(0);\n  let stickyValue = (filter, input=filterInput(filter)) => $(filter).on('change', (evt, param) => {\n    let _input = filterInput(filter);\n    if (evt.removed?.id?.startsWith('order_by_') == evt.val?.startsWith('order_by_'))\n      _input.val( input.val() );  // retaining the last value\n    input = _input;\n    if (input.prop('tagName') == 'SELECT')  // redraw select widget\n      $(`.select2-chosen`, input.prev()).text( $(':selected', input).text() );\n  });\n  orderFilters().each(function () {stickyValue(this)});\n  adder.onclick = () => setTimeout(() => stickyValue( orderFilters().last() ));\n});\n"
  },
  {
    "path": "bukuserver/templates/bukuserver/bookmark_create.html",
    "content": "{% extends 'admin/model/create.html' %}\n{% import 'bukuserver/lib.html' as buku with context %}\n\n{% block tail %}\n  {{ super() }}\n  {{ buku.set_lang() }}\n  {{ buku.limit_navigation_if_popup() }}\n  {{ buku.brand_dbname() }}\n  {{ buku.script('bookmark.js') }}\n  {{ buku.fetch_checkbox(form.fetch.data) }}\n  {{ buku.horizontal_form(excluding_popups=True) }}\n  {{ buku.focus() }}\n  {{ buku.link_saved() }}\n{% endblock %}\n"
  },
  {
    "path": "bukuserver/templates/bukuserver/bookmark_create_modal.html",
    "content": "{% extends 'admin/model/modals/create.html' %}\n{% import 'bukuserver/lib.html' as buku with context %}\n\n{% block create_form %}\n  {{ lib.render_form(form, return_url, lib.extra(), form_opts=form_opts,\n                     action=url_for('.create_view', url=return_url),\n                     is_modal=True) }}\n{% endblock %}\n\n{% block tail %}\n  {{ super() }}\n  {{ buku.script('bookmark.js') }}\n  {{ buku.fetch_checkbox(form.fetch.data, modal=True) }}\n  {{ buku.horizontal_form() }}\n  {{ buku.focus('.modal-body') }}\n{% endblock %}\n"
  },
  {
    "path": "bukuserver/templates/bukuserver/bookmark_details.html",
    "content": "{% extends 'admin/model/details.html' %}\n{% import 'bukuserver/lib.html' as buku with context %}\n\n{% block tail %}\n  {{ super() }}\n  {{ buku.set_lang() }}\n  {{ buku.limit_navigation_if_popup() }}\n  {{ buku.brand_dbname() }}\n  {{ buku.details_formatting('.table.searchable') }}\n  <script>$('#fa_filter, .table.searchable a').attr('tabindex', 1)</script>\n  {{ buku.link_saved() }}\n{% endblock %}\n"
  },
  {
    "path": "bukuserver/templates/bukuserver/bookmark_details_modal.html",
    "content": "{% extends 'admin/model/modals/details.html' %}\n{% import 'bukuserver/lib.html' as buku with context %}\n\n{% block header_text %}\n  <h3>\n    {% if request.args.get('id') == 'random' %}\n    <button id=\"modal-random\" class=\"btn btn-secondary\" title=\"{{ _('Pick another') }}\">\n      <span class=\"fa fa-repeat glyphicon glyphicon-repeat\"></span>\n    </button>\n    {% endif %}\n    <a href=\"{{ url_for('bookmark.details_view', id=model.id, url=request.args.url) }}\">{{_gettext('View Record')}} #{{model.id}}</a>\n  </h3>\n{% endblock %}\n\n{% block tail %}\n  {{ super() }}\n  {{ buku.details_formatting('.modal') }}\n  {{ buku.focus('.modal-body') }}\n{% endblock %}\n"
  },
  {
    "path": "bukuserver/templates/bukuserver/bookmark_edit.html",
    "content": "{% extends 'admin/model/edit.html' %}\n{% import 'bukuserver/lib.html' as buku with context %}\n\n{% block head %}\n  {{ super() }}\n  {{ buku.brand_dbname() }}\n{% endblock %}\n\n{% block edit_form %}\n  {{ super() }}\n  <form method=\"POST\" action=\"{{ get_url('.delete_view') }}\" class=\"delete-form d-inline-block float-right\">\n    <input type=\"hidden\" name=\"id\" value=\"{{ request.args.get('id') }}\"/>\n    <input type=\"hidden\" name=\"url\" value=\"{{ return_url }}\"/>\n    {% if csrf_token %}\n    <input type=\"hidden\" name=\"csrf_token\" value=\"{{ csrf_token() }}\"/>\n    {% endif %}\n    <button class=\"btn btn-warning\" onclick=\"return faHelpers.safeConfirm('{{ _gettext('Are you sure you want to delete this record?') }}');\">\n      {{ _gettext('Delete') }}\n    </button>\n  </form>\n{% endblock %}\n\n{% block tail %}\n  {{ super() }}\n  {{ buku.set_lang() }}\n  {{ buku.limit_navigation_if_popup() }}\n  {{ buku.script('bookmark.js') }}\n  <script>$('.submit-row').append($('.delete-form'))</script>\n  {{ buku.horizontal_form(excluding_popups=True) }}\n  {{ buku.focus() }}\n  {{ buku.link_saved() }}\n{% endblock %}\n"
  },
  {
    "path": "bukuserver/templates/bukuserver/bookmark_edit_modal.html",
    "content": "{% extends 'admin/model/modals/edit.html' %}\n{% import 'bukuserver/lib.html' as buku with context %}\n\n{% block tail %}\n  {{ super() }}\n  {{ buku.script('bookmark.js') }}\n  {{ buku.horizontal_form() }}\n  {{ buku.focus('.modal-body') }}\n{% endblock %}\n"
  },
  {
    "path": "bukuserver/templates/bukuserver/bookmarklet.url",
    "content": "javascript:void%20function(){var%20e=location.href,t=document.title.trim()||%22%22,o=document.getSelection().toString().trim()||(document.querySelector(%22meta[name$=description%20i],%20meta[property$=description%20i]%22)||{}).content||%22%22;o.length%3E4e3%26%26(o=o.substr(0,4e3)+%22...%22,alert(%22The%20selected%20text%20is%20too%20long,%20it%20will%20be%20truncated.%22)),e=%22{{url}}%3Furl=%22+encodeURIComponent(e)+%22%26title=%22+encodeURIComponent(t)+%22%26description=%22+encodeURIComponent(o),window.open(e,%22_blank%22,%22menubar=no,%20height=700,%20width=800,%20toolbar=no,%20scrollbars=yes,%20status=no,%20dialog=1%22)}();\n"
  },
  {
    "path": "bukuserver/templates/bukuserver/bookmarks_list.html",
    "content": "{% extends 'admin/model/list.html' %}\n{% import 'bukuserver/lib.html' as buku with context %}\n\n{% block head %}\n  {{ super() }}\n  {{ buku.close_if_popup() }}\n  {{ buku.brand_dbname() }}\n  <script>\n    function promptSwap(input, rowId, maxId={{count|tojson}}) {\n      let _id = input.value = prompt({{ _('Swap record #{} with record #')|tojson }}.replace('{}', rowId), rowId) || \"\";\n      let error = (!_id                       ? \"\" :\n                   !/^[1-9][0-9]*$/.test(_id) ? {{ _(\"Not a valid record index: '{}'\")|tojson }}.replace('{}', _id) :\n                   _id > maxId                ? {{ _('There are only {} records in total!')|tojson }}.replace('{}', maxId) :\n                   _id == `${rowId}`          ? {{ _('Swapping a record with itself has no effect!')|tojson }} : null);\n      error && alert(error);\n      return (error == null);\n    }\n  </script>\n{% endblock %}\n\n{% block model_menu_bar_before_filters %}\n  {{ super() }}\n  {% if data %}\n    {% set _random = url_for('.details_view', modal=True, id='random', url=return_url, **(request.args|flt)) %}\n    <li class=\"nav-item\">\n      <a id=\"random\" class=\"nav-link\" data-target=\"#fa_modal_window\" data-toggle=\"modal\" href=\"{{ _random }}\">{{ _('Random') }}</a>\n    </li>\n  {% endif %}\n  <li id=\"reorderButton\" class=\"nav-item d-none\">\n    <form id=\"reorder\" class=\"d-none\" method=\"POST\" action=\"reorder\">\n      <input type=\"hidden\" name=\"filters\"/>\n    </form>\n    <a class=\"nav-link\" href=\"#\" title=\"{{ _('Update indices to match this order') }}\"\n       onclick=\"reorder.confirmAndSubmit()\">{{ _('Reorder') }}</a>\n  </li>\n  <script>\n    reorder.confirmAndSubmit = () => confirm({{ _('Save this order in DB?') | tojson }}) && reorder.submit();\n    addEventListener('DOMContentLoaded', () => {\n      let flt = JSON.parse( $('#active-filters-data').attr('data-initial') );\n      if ((flt.length > 0) && flt.every(x => x[1] == 'order')) {\n        reorderButton.classList.remove('d-none');\n        $('#reorder [name=filters]').val(JSON.stringify(flt));\n        $(`.field-filters .filter, .filters .remove-filter`).on('click', () => reorderButton.classList.add('d-none'));\n        $(`.filters select`).on('change', () => reorderButton.classList.add('d-none'));\n        $(`.filters input`).on('input', () => reorderButton.classList.add('d-none'));\n      }\n    });\n  </script>\n{% endblock %}\n\n{% macro swap_rows_action(icon, row_id, step=None) %}  {# based on admin/model/row_actions.delete_row() #}\n<form class=\"icon\" method=\"POST\" action=\"{{ get_url('.swap') }}\">\n  {% if csrf_token %}\n  <input type=\"hidden\" name=\"csrf_token\" value=\"{{ csrf_token() }}\"/>\n  {% endif %}\n  {% set _input = 'swap' + row_id|string %}\n  <input type=\"hidden\" name=\"url\" value=\"{{ return_url }}\"/>\n  <input type=\"hidden\" name=\"id1\" value=\"{{ row_id }}\"/>\n  <input type=\"hidden\" name=\"id2\"{% if step %} value=\"{{ row_id + step }}\"{% else %} id=\"{{ _input }}\"{% endif %}/>\n  <button title=\"{{ _('Swap with…') if not step else _('Move down') if step > 0 else _('Move up') }}\"\n          {%- if not step %} onclick=\"return promptSwap({{_input}}, {{row_id}})\"{% endif %}>\n    <span class=\"fa fa-{{icon}} glyphicon glyphicon-{{icon}}\"></span>\n  </button>\n</form>\n{% endmacro %}\n\n{% block list_row_actions scoped %}\n  {% for action in list_row_actions %}\n    {{ action.render_ctx(get_pk_value(row), row) }}\n  {% endfor %}\n  {% if request.args|flt|length == 0 %}  {# only shown when filters/ordering are disabled #}\n    <div class=\"swap-toolbar\" style=\"margin-left: 9px\">\n      {% if row.id < 2 %}\n        <div class=\"d-inline-block\" style=\"width: 14px\"><!-- placeholder for the 1st row button --></div>\n      {% else %}\n        {{ swap_rows_action('arrow-up', row.id, -1) }}\n      {% endif %}\n      {{ swap_rows_action('exchange', row.id) }}\n      {% if row.id < count %}\n        {{ swap_rows_action('arrow-down', row.id, +1) }}\n      {% endif %}\n    </div>\n  {% endif %}\n{% endblock %}\n\n{% block tail %}\n  {{ buku.fix_translations('bookmarks') }}\n  {{ super() }}\n  {{ buku.page_size_custom() }}\n  {{ buku.script('buku_filter.js') }}\n  {{ buku.script('order_filter.js') }}\n  {{ buku.focus(None) }}\n  {{ buku.link_saved() }}\n  <script>\n    $(document).on('click', `#modal-random`, function() {\n      $(`#fa_modal_window .modal-content`).load($(`#random`).attr('href'));\n    });\n  </script>\n{% endblock %}\n"
  },
  {
    "path": "bukuserver/templates/bukuserver/home.html",
    "content": "{% extends \"admin/index.html\" %}\n{% import 'bukuserver/lib.html' as buku with context %}\n\n{% block head %}\n  {{ super() }}\n  {{ buku.close_if_popup() }}\n  {{ buku.brand_dbname() }}\n  {{ buku.focus('main form[action=\"/\"]') }}\n{% endblock %}\n\n{% block menu_links %}\n{{ super() }}\n<form class=\"form-inline navbar-right\" style=\"gap:.275rem\" action=\"{{ url_for('admin.search') }}\" method=\"POST\">\n  <div class=\"d-inline-block align-middle\">\n    <input class=\"form-control\" id=\"inputKeywords\" placeholder=\"{{ _('Search bookmark') }}\" name=\"keyword\"/>\n    <input type=\"hidden\" name=\"markers\" value=\"true\"/>\n    <input type=\"hidden\" name=\"all_keywords\" value=\"true\"/>\n  </div>\n  <button type=\"submit\" class=\"btn btn-secondary\">{{ _gettext('Search') }}</button>\n</form>\n{% endblock %}\n\n{% block body %}\n{{ super() }}\n<main class=\"container text-center p-4\">\n    <h1>BUKU</h1>\n    <p class=\"lead\">{{ _('Bookmark manager like a text-based mini-web') }}</p>\n    <p>\n      <a class=\"btn btn-lg btn-success\" href=\"{{ url_for('bookmark.index_view') }}\" role=\"button\">{{ _('Bookmarks') }}</a>\n      <a class=\"btn btn-lg btn-success\" href=\"{{ url_for('tag.index_view') }}\" role=\"button\">{{ _('Tags') }}</a>\n      <a class=\"btn btn-lg btn-success\" href=\"{{ url_for('statistic.index') }}\" role=\"button\">{{ _('Statistic') }}</a>\n    </p>\n    <div class=\"col-md-4 offset-md-4\">\n      <form action=\"{{ url_for('admin.search') }}\" method=\"POST\">\n        <div class=\"form-group\">\n          {{ form.keyword.label }}\n          {{ form.keyword(class_='form-control d-inline', style='width: auto') }}\n        </div>\n        <div class=\"text-left\">\n          {% for field in [form.all_keywords, form.markers, form.deep, form.regex] -%}\n          <div class=\"form-check\" title=\"{{ field.description }}\" data-toggle=\"tooltip\" data-placement=\"bottom\"> {{field()}} {{field.label}} </div>\n          {%- endfor %}\n        </div>\n        <button type=\"submit\" class=\"btn btn-secondary\">{{ _gettext('Search') }}</button>\n      </form>\n    </div>\n    <div class=\"col-md-4 offset-md-4\">\n      <p class=\"pt-4\"> {{_('Bookmarklet')}}:\n        <a title=\"Drag this link to your bookmarks toolbar\" href=\"{{ buku.bookmarklet() }}\">\n          <b>✚ {{ _('Add to Buku') }}</b>\n        </a><br/>\n        <em style=\"font-size: smaller\">{{ _(\"Note: if you select text on the page before activating the bookmarklet, it'll be used as description instead of page metadata.\") }}</em>\n      </p>\n    </div>\n\n    <details class=\"col-md-6 offset-md-3\">\n      <summary style=\"display: list-item;  cursor: pointer\"> <em><strong>{{ _('Location Bar (keyboard-only) shortcut') }}</strong></em> </summary>\n      <dl>\n        <dt>{{ _('in Firefox:') }}</dt>\n        <dd>{{ _('Open the bookmarks editor and set %(buku)s in the Keyword field of the bookmarklet.', buku='<code>@buku</code>'|safe) }}</dd>\n\n        <dt>{{ _('in Chrome:') }}</dt>\n        <dd>\n          {{ _('In %(path)s, add a new row by placing %(add_to_buku)s, %(buku)s, and the copied bookmarklet URL in respective fields).',\n               path='<em>'|safe + _('Settings > Search engine > Manage… > Site Search')|escape + '</em>'|safe,\n               add_to_buku='<code>✚ '|safe + _('Add to Buku') + '</code>'|safe, buku='<code>@buku</code>'|safe) }}\n        </dd>\n\n        <dt>{{ _('usage:') }}</dt>\n        <dd>\n          {{ _(\"By hitting %(hotkey)s (thus switching to Location Bar), then typing %(buku)s and hitting %(enter)s, you'll be able to open the bookmarklet dialog via keyboard only.\",\n               hotkey='<code>Ctrl+L</code>'|safe, buku='<code>@buku</code>'|safe, enter='<code>Enter</code>'|safe) }}\n          <br/><em style=\"font-size: smaller\">{{\n            _('Note: in Firefox this changes displayed URL, but you can reset it by switching back to Location Bar and hitting %(escape)s twice.', escape='<code>Esc</code>'|safe)\n          }}</em>\n        </dd>\n      </dl>\n    </details>\n</main>\n{% endblock %}\n\n{% block tail %}\n  {{ buku.set_lang() }}\n  <script>\n    $(`[data-toggle=\"tooltip\"]`).attr('data-html', 'true').each(function () {\n      this.title = this.title.replace(/'(.*?)'/g, `'<strong><code>$1</code></strong>'`)\n        .replace(/(?<=^|[^\\p{L}]){{ _('FULL') }}(?=$|[^\\p{L}])/g, `<strong><em>{{ _('FULL')|lower }}</em></strong>`);\n    }).attr('data-container', 'body').attr('data-trigger', 'hover').tooltip();\n  </script>\n  <style>.tooltip-inner {text-align: left;  white-space: pre;  max-width: 600px}</style>\n{% endblock %}\n"
  },
  {
    "path": "bukuserver/templates/bukuserver/lib.html",
    "content": "{% macro filter(name, value) %}{{ url_for('bookmark.index_view', **{'flt0_'+name: value}) }}{% endmacro %}\n\n{% macro bookmarklet() -%}\n  {% with url = url_for('bookmarklet', _external=True) -%}\n    {% include 'bukuserver/bookmarklet.url' %}\n  {%- endwith %}\n{%- endmacro %}\n\n{% macro script(filename) %}\n  <script src=\"{{ url_for('static', filename='bukuserver/js/'+filename) }}\"></script>\n{% endmacro %}\n\n{% macro close_if_popup() %}\n  <script>({{ g.popup|default|tojson }} || (opener && (opener !== window))) && close()</script>\n{% endmacro %}\n\n{% macro limit_navigation_if_popup() %}\n  <style>\n    .popup a.navbar-brand {pointer-events: none}\n    .popup #admin-navbar-collapse {display: block}\n    .popup :is(.navbar-nav, .navbar-toggler) {display: none}\n    .popup .nav-tabs :is(:first-child, :nth-child(2)) > .nav-link:not(.active) {display: none}\n  </style>\n  <script>\n    const POPUP = {{ g.popup|default|tojson }} || (opener && (opener !== window)) || '';\n    POPUP && (document.body.classList.add('popup'), setTimeout(() => {\n      $(`.nav-tabs a:not([href^='javascript:'])`).each((_, e) => e.href += `&popup=${POPUP}`);\n      $(`.submit-row .btn-danger`).on('click', () => close());\n      $(`.delete-form`).prepend(`<input type=\"hidden\" name=\"popup\" value=\"${POPUP}\">`);\n    }));\n  </script>\n{% endmacro %}\n\n{% macro brand_dbname(nohover=False) %}\n  <style>\n    .dbname {font-weight: bold;  font-family: monospace;  cursor: help}\n    .navbar-brand .dbname {font-size: medium}\n    body:not(.popup) .navbar-brand {text-align: center}\n    body.popup .navbar-brand .dbname, body:not(.popup) .popup.dbname {display: none}\n    .popup.dbname {float: right;  font-size: smaller;  line-height: 2.2}\n    {% if nohover %}\n    body:not(.popup) .navbar-brand {padding-top: 0;  padding-bottom: 0;  margin-top: -.625rem;  margin-bottom: -.625rem}\n    {% else %}\n    body:not(.popup) .navbar-brand:hover {padding-top: 0;  padding-bottom: 0;  margin-top: -.625rem;  margin-bottom: -.625rem}\n    body:not(.popup) .navbar-brand:not(:hover) .dbname {display: none}\n    {% endif %}\n  </style>\n  <script>{\n    let _dbname = (cls='') => `<div class=\"dbname ${cls}\" title=\"${ {{dbfile|tojson}} }\">${ {{dbname|tojson}} }</div>`;\n    addEventListener('load', () => {$('.navbar-brand').html((_, s) => s + _dbname());\n                                    $('.navbar-collapse').html((_, s) => s + _dbname('popup navbar-brand'))});\n  }</script>\n{% endmacro %}\n\n{% macro focus(location='body') %}\n  {% if location %}\n  <script>setTimeout(() => $('{{location|safe}} input:not([type=hidden])')[0]?.focus(), 500)</script>\n  {% else %}\n  <script>$(document).ready(() => document.activeElement?.blur())</script>\n  {% endif %}\n{% endmacro %}\n\n{% macro fetch_checkbox(checked=True, modal=False) %}\n  <script>{\n    let tooltip = {{ _('Collect missing data (+extra tags) by fetching & parsing the webpage')|tojson }}\n    $('.admin-form [name=fetch]').remove();\n    $('.admin-form fieldset{% if modal %} .modal-footer{% endif %}').{% if modal %}prepend{% else %}append{% endif %}(\n      $(`<div class=\"form-group {{ 'mb-0' if modal else '' }}\"{% if modal %} style=\"flex-grow: 1\"{% endif %} title=\"${tooltip}\">`\n        +`<label class=\"control-label {{ 'mb-0' if modal else '' }}\">{{ _('Fetch') }} &nbsp; </label>`\n        +`<input type=\"checkbox\" name=\"fetch\"{% if checked %} checked{% endif %}></div>`));\n  }</script>\n{% endmacro %}\n\n{% macro horizontal_form(excluding_popups=False) %}\n  <script>\n    $('.admin-form .form-group').each(function () {\n      if ($('.submit-row', this).length > 0{% if excluding_popups %} || document.body.matches('.popup'){% endif %}) {\n        $('.submit-row', this).addClass(document.body.matches('.popup') ? 'col-md-12' : 'offset-md-2');\n      } else if ($('input[type=checkbox], input[type=radio]', this).length > 0) {\n        $('label', this).addClass('form-row').css({cursor: 'pointer'}).html(`<span>${ $('label', this).html() }</span>`);\n        $('label span', this).addClass('col-md-2 col-form-label text-right d-inline-block');\n        $('input', this).appendTo($('label', this));\n      } else {\n        $(this).addClass('form-row');\n        $('input, textarea, select', this).addClass('col-md-10');\n        $('label', this).addClass('col-md-2 col-form-label text-right');\n      }\n    });\n  </script>\n{% endmacro %}\n\n{% macro details_formatting(prefix='') %}\n  <script>\n    $(`.modal-header h3`).wrapInner('<h5 class=\"modal-title\">').children(0).unwrap();  // flask-admin #2505\n    $(`body.popup {{prefix}} a`).attr('target', '_blank');\n  </script>\n{% endmacro %}\n\n{% macro link_saved() %}\n  {% set backlink = request.args.get('url', request.full_path) %}\n  {% set saved = session.pop('saved', None) %}\n  {% if saved %}\n  <script>{\n    const SUCCESS = [{{ _gettext('Record was successfully created.') | tojson }},\n                     {{ _gettext('Record was successfully saved.') | tojson }}];\n    $(`.alert-success`).filter((idx, e) => SUCCESS.some(s => e.innerText.includes(s))).html((_, s) =>\n      s.replace(/(<\\/button>)([^]*)$/, `$1<a href=\"{{ url_for('.details_view', id=saved, url=backlink, popup=g.popup) }}\">$2</a>`));\n  }</script>\n  {% endif %}\n{% endmacro %}\n\n{% macro set_lang() %}\n  <script>document.documentElement.lang = {{ lang|tojson }}</script>\n{% endmacro %}\n\n{% macro fix_translations(list) %}  {# invoke in *_list.html BEFORE tail.super()! #}\n  {{ set_lang() }}\n  {% set LISTS = {'tags': {'name': _p('tags', 'name'), 'usage_count': _p('tags', 'usage count')},\n                  'bookmarks': {'id': _p('bookmarks', 'index'), 'url': _p('bookmarks', 'url'), 'title': _p('bookmarks', 'title'),\n                                'tags': _p('bookmarks', 'tags'), 'order': _p('bookmarks', 'order')}} %}\n  <script>{\n    $(`button[title=\"Delete record\"]`).attr('title', {{ _('Delete record')|tojson }});  // see flask-admin issue #1974\n    const FILTERS = {{ LISTS.get(list, {})|tojson }};\n    let watcher = new MutationObserver(xs => xs.forEach(x => x.addedNodes.forEach(e => {\n      if (!['filter-groups-data', 'active-filters-data'].includes(e.id)) return;\n      e.setAttribute('data-initial', e.innerHTML);\n      let data = JSON.parse(e.innerHTML);\n      let converted = (e.id == 'active-filters-data' ? data.map(([id, name, value]) => [id, FILTERS[name]||name, value]) :\n                       Object.fromEntries( Object.entries(data).map(([k, v]) => [FILTERS[k]||k, v]) ));\n      e.innerHTML = JSON.stringify(converted);\n    })));\n    watcher.observe(document.body, {childList: true, subtree: true});\n    setTimeout(() => watcher.disconnect());  // will stop observing once <body> is rendered\n    $(document).ready(() => {\n      $(`.field-filters .filter`).each(function () {\n        let text = $(this).text();\n        $(this).text(FILTERS[text] || text);\n      });\n    })\n  }</script>\n{% endmacro %}\n\n{% macro page_size_custom() %}\n  <script>\n    $(document).ready(function() {\n      let pageSize = url => new URL(url || location.host).searchParams.get('page_size');\n      $(`.nav.nav-tabs .dropdown-menu`).each(function () {\n        let _sizes = $(`a.dropdown-item`, this).map(function () {return pageSize(this.href)}).get();\n        if (_sizes.length > 2 && _sizes.length == new Set(_sizes).size)  // 3+ links; each link has different pagesize\n          $('a', this).last().clone().text({{ _('custom')|tojson }}).removeClass('active').attr('href', `#`).on('click', () => {\n            let page = prompt({{ _('Set custom page size (empty for default)')|tojson }}, pageSize(location) || '');\n            if (Number(page) || (page == \"\")) {\n              let search = new URL(location).searchParams;\n              (page ? search.set('page_size', page) : search.delete('page_size'));\n              location.search = search;\n            } else if (page != null)\n              alert({{ _('Invalid page size')|tojson }} + `: \"${page}\"`);\n            return false;\n          }).appendTo(this);\n      })\n    });\n  </script>\n{% endmacro %}\n"
  },
  {
    "path": "bukuserver/templates/bukuserver/statistic.html",
    "content": "{% extends \"bukuserver/home.html\" %}\n{% import 'bukuserver/lib.html' as buku with context %}\n\n{% block head %}\n  {{ super() }}\n  {{ buku.close_if_popup() }}\n  <script>{// realtime redrawing \"Data created\" datetime\n    const UNITS = {day: 60*60*24, hour: 60*60, minute: 60, second: 1};\n    const AGO = {{ {'day': _('{} days ago'), 'hour': _('{} hours ago'), 'minute': _('{} minutes ago'), 'second': _('{} seconds ago')}|tojson }};\n    addEventListener('load', function recalcReltime() {\n      let diff = Date.now()/1000 - created.getAttribute('data-timestamp');\n      let unit = (diff < 5 ? \"\" : Object.keys(UNITS).find(k => diff >= UNITS[k]));\n      created.innerText = (!unit ? {{ _('just now')|tojson }} : AGO[unit].replace('{}', parseInt(diff/UNITS[unit])));\n      setTimeout(recalcReltime, 1000 * {second: 1, minute: 15, hour: 60, day: 15*60}[unit||'second']);\n    });\n  }</script>\n{% endblock %}\n\n{% block body %}\n<div class=\"container mb-4\">\n  <form class=\"mb-4\" action=\"{{ url_for('statistic.index') }}\" method=\"POST\">\n    {{ _('Data created') }}\n    <span id=\"created\" rel=\"tooltip\" title=\"{{ datetime }}\" data-timestamp={{ datetime.timestamp() }}>{{ datetime_text }}</span>\n    <button type=\"submit\" class=\"btn btn-secondary btn-sm\">{{ _('Refresh')|lower }}</button>\n  </form>\n  <h3>{{ _('Netloc') }}</h3>\n\n  {% if netlocs %}\n  <div class=\"row\">\n    <div class=\"col-md-6\">\n      <canvas id=\"mostCommonChart\" width=\"500\" height=\"500\"></canvas>\n    </div>\n\n    <div class=\"col-md-6\">\n      {% if netlocs.cropped %}\n      <button type=\"button\" class=\"btn btn-primary btn-sm\" data-toggle=\"modal\" data-target=\"#netlocModal\">\n        {{ _('View all') }}\n      </button>\n      {% endif %}\n      <table class=\"table\">\n        <thead>\n          <tr>\n            <th>{{ _('Rank') }}</th>\n            <th>{{ _('Netloc') }}</th>\n            <th class=\"text-right\">{{ _('Number') }}</th>\n          </tr>\n        </thead>\n        <tbody>\n          {% for item in netlocs %}\n          <tr>\n            <td>{{ loop.index }}</td>\n            <td> <a href=\"{{ buku.filter('url_netloc_match', item.name) }}\">{{ item.name or _('(no netloc)') }}</a> </td>\n            <td class=\"text-right\">{{ item.amount }}</td>\n          </tr>\n          {% endfor %}\n        </tbody>\n      </table>\n    </div>\n  </div>\n  {% else %}\n  <span>{{ _('No bookmarks found.') }}</span>\n  {% endif %}\n\n  {% if netlocs.cropped %}\n  <div class=\"modal fade\" id=\"netlocModal\" tabindex=\"-1\" role=\"dialog\">\n    <div class=\"modal-dialog\" role=\"document\">\n      <div class=\"modal-content\">\n        <div class=\"modal-header\">\n          <h4 class=\"modal-title\" id=\"myModalLabel\">{{ _('Netloc ranking') }}</h4>\n          <button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\"><span aria-hidden=\"true\">&times;</span></button>\n        </div>\n        <div class=\"modal-body\">\n          <table class=\"table table-sm\">\n            <thead class=\"thead-dark\">\n              <tr>\n                <th>{{ _('Rank') }}</th>\n                <th>{{ _('Netloc') }}</th>\n                <th class=\"text-right\">{{ _('Number') }}</th>\n              </tr>\n            </thead>\n            <tbody>\n              {% for name, amount in netlocs.all %}\n              <tr>\n                <td>{{ loop.index }}</td>\n                <td>\n                  <a href=\"{{ buku.filter('url_netloc_match', name) }}\">{{ name or _('(no netloc)') }}</a>\n                </td>\n                <td class=\"text-right\">{{ amount }}</td>\n              </tr>\n              {% endfor %}\n            </tbody>\n          </table>\n        </div>\n      </div>\n    </div>\n  </div>\n  {% endif %}\n\n  <h3 class=\"col-md-12\">{{ _('Tag') }}</h3>\n\n  {% if tags %}\n  <div class=\"row\">\n    <div class=\"col-md-6\">\n      <canvas id=\"mostCommonTagChart\" width=\"500\" height=\"500\"></canvas>\n    </div>\n\n    <div class=\"col-md-6\">\n      {% if tags.cropped %}\n      <button type=\"button\" class=\"btn btn-primary btn-sm\" data-toggle=\"modal\" data-target=\"#tagRankModal\">\n        {{ _('View all') }}\n      </button>\n      {% endif %}\n      <table class=\"table\">\n        <thead>\n          <tr>\n            <th>{{ _('Rank') }}</th>\n            <th>{{ _('Tag') }}</th>\n            <th class=\"text-right\">{{ _('Number') }}</th>\n          </tr>\n        </thead>\n        <tbody>\n          {% for item in tags %}\n          <tr>\n            <td>{{ loop.index }}</td>\n            <td>\n              <a href=\"{{ buku.filter('tags_contain', item.name) }}\">{{ item.name }}</a>\n            </td>\n            <td class=\"text-right\">{{ item.amount }}</td>\n          </tr>\n          {% endfor %}\n        </tbody>\n      </table>\n    </div>\n  </div>\n  {% else %}\n  <span>{{ _('No tags found.') }}</span>\n  {% endif %}\n\n  {% if tags.cropped %}\n  <div class=\"modal fade\" id=\"tagRankModal\" tabindex=\"-1\" role=\"dialog\">\n    <div class=\"modal-dialog\" role=\"document\">\n      <div class=\"modal-content\">\n        <div class=\"modal-header\">\n          <h4 class=\"modal-title\" id=\"myModalLabel\">{{ _('Tag ranking') }}</h4>\n          <button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\"><span aria-hidden=\"true\">&times;</span></button>\n        </div>\n        <div class=\"modal-body\">\n          <table class=\"table table-sm\">\n            <thead class=\"thead-dark\">\n              <tr>\n                <th>{{ _('Rank') }}</th>\n                <th>{{ _('Tag') }}</th>\n                <th class=\"text-right\">{{ _('Number') }}</th>\n              </tr>\n            </thead>\n            <tbody>\n              {% for name, amount in tags.all %}\n              <tr>\n                <td>{{ loop.index }}</td>\n                <td> <a href=\"{{ buku.filter('tags_contain', name) }}\">{{ name }}</a> </td>\n                <td class=\"text-right\">{{ amount }}</td>\n              </tr>\n              {% endfor %}\n            </tbody>\n          </table>\n        </div>\n      </div>\n    </div>\n  </div>\n  {% endif %}\n\n  <h3 class=\"col-md-12\">{{ _('Title (common)') }}</h3>\n\n  {% if titles %}\n  <div class=\"row\">\n    <div class=\"col-md-6\">\n      <canvas id=\"mostCommonTitleChart\" width=\"500\" height=\"500\"></canvas>\n    </div>\n\n    <div class=\"col-md-6\">\n      {% if titles.cropped %}\n      <button type=\"button\" class=\"btn btn-primary btn-sm\" data-toggle=\"modal\" data-target=\"#titleModal\">\n        {{ _('View all') }}\n      </button>\n      {% endif %}\n      <table class=\"table\">\n        <thead>\n          <tr>\n            <th>{{ _('Rank') }}</th>\n            <th>{{ _('Title') }}</th>\n            <th class=\"text-right\">{{ _('Number') }}</th>\n          </tr>\n        </thead>\n        <tbody>\n          {% for item in titles %}\n          <tr>\n            <td>{{ loop.index }}</td>\n            <td>\n              <a href=\"{{ buku.filter('title_equals', item.name) }}\">{{ item.name or _('(no title)')}}</a>\n            </td>\n            <td class=\"text-right\">{{ item.amount }}</td>\n          </tr>\n          {% endfor %}\n        </tbody>\n      </table>\n    </div>\n  </div>\n  {% else %}\n  <span>{{ _('No common titles found.') }}</span>\n  {% endif %}\n\n  {% if titles.cropped %}\n  <div class=\"modal fade\" id=\"titleModal\" tabindex=\"-1\" role=\"dialog\">\n    <div class=\"modal-dialog\" role=\"document\">\n      <div class=\"modal-content\">\n        <div class=\"modal-header\">\n          <h4 class=\"modal-title\" id=\"myModalLabel\">{{ _('Common titles ranking') }}</h4>\n          <button type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\"><span aria-hidden=\"true\">&times;</span></button>\n        </div>\n        <div class=\"modal-body\">\n          <table class=\"table table-sm\">\n            <thead class=\"thead-dark\">\n              <tr>\n                <th>{{ _('Rank') }}</th>\n                <th>{{ _('Title') }}</th>\n                <th class=\"text-right\">{{ _('Number') }}</th>\n              </tr>\n            </thead>\n            <tbody>\n              {% for name, amount in titles.all %}\n              <tr>\n                <td>{{ loop.index }}</td>\n                <td style=\"word-break:break-all;\">\n                  <a href=\"{{ buku.filter('title_equals', name) }}\">{{ name or _('(no title)') }}</a>\n                </td>\n                <td class=\"text-right\">{{ amount }}</td>\n              </tr>\n              {% endfor %}\n            </tbody>\n          </table>\n        </div>\n      </div>\n    </div>\n  </div>\n  {% endif %}\n{% endblock %}\n\n{% block tail %}\n  {{ super() }}\n  {{ buku.set_lang() }}\n  <script>// sticky table headers in modals\n    $('.modal-body').each(function () {\n      $('thead', this).css({top: `calc(-${$.css(this, 'padding-top')} - 1px)`});  // +border\n    });\n  </script>\n  {{ buku.script('Chart.js') }}\n  <script>\n  {% set NO_NETLOC = '\\u200B' + _('(no netloc)') + '\\u200B' -%}\n  var netlocCtx = document.getElementById(\"mostCommonChart\")?.getContext('2d');\n  var netlocChart = netlocCtx && new Chart(netlocCtx, {\n    type: 'pie',\n    data: {\n      datasets: [{\n        data: [\n          {% for val in netlocs %} {{val.amount}}, {% endfor %}\n        ],\n        backgroundColor: [\n          {% for val in netlocs %} {{val.color|tojson}}, {% endfor %}\n        ],\n      }],\n      // These labels appear in the legend and in the tooltips when hovering different arcs\n      labels: [\n        {% for val in netlocs %} {{(val.name or NO_NETLOC)|tojson}}, {% endfor %}\n      ]\n    },\n    options: {\n      onClick (evt, item) {\n        if (!item[0]) return;\n        var value = this.data.labels[item[0]._index].replace({{NO_NETLOC|tojson}}, \"\");\n        var form = $('<form></form>');\n\n        form.attr(\"method\", \"get\");\n        form.attr(\"action\", \"{{url_for('bookmark.index_view')}}\");\n\n        var field = $('<input></input>');\n\n        field.attr(\"type\", \"hidden\");\n        field.attr(\"name\", \"flt0_url_netloc_match\");\n        field.attr(\"value\", value);\n        form.append(field);\n\n        // The form needs to be a part of the document in\n        // order for us to be able to submit it.\n        $(document.body).append(form);\n        form.submit();\n      }\n    }\n  });\n\n  var tagRankCtx = document.getElementById(\"mostCommonTagChart\")?.getContext('2d');\n  var tagRankChart = tagRankCtx && new Chart(tagRankCtx, {\n    type: 'pie',\n    data: {\n      datasets: [{\n        data: [\n          {% for val in tags %} {{val.amount}}, {% endfor %}\n        ],\n        backgroundColor: [\n          {% for val in tags %} {{val.color|tojson}}, {% endfor %}\n        ],\n      }],\n      // These labels appear in the legend and in the tooltips when hovering different arcs\n      labels: [\n        {% for val in tags %} {{val.name|tojson}}, {% endfor %}\n      ]\n    },\n    options: {\n      onClick (evt, item) {\n        if (!item[0]) return;\n        var tagStr = this.data.labels[item[0]._index];\n        var url = \"{{url_for('bookmark.index_view')}}?flt0_tags_contain=\" + encodeURIComponent(tagStr);\n        window.location.href = url;\n      }\n    }\n  });\n\n  {% set NO_TITLE = '\\u200B' + _('(no title)') + '\\u200B' -%}\n  var titleCtx = document.getElementById(\"mostCommonTitleChart\")?.getContext('2d');\n  var titleChart = titleCtx && new Chart(titleCtx, {\n    type: 'pie',\n    data: {\n      datasets: [{\n        data: [\n          {% for val in titles %} {{val.amount}}, {% endfor %}\n        ],\n        backgroundColor: [\n          {% for val in titles %} {{val.color|tojson}}, {% endfor %}\n        ],\n      }],\n      // These labels appear in the legend and in the tooltips when hovering different arcs\n      labels: [\n        {% for val in titles %} {{(val.name|trim or NO_TITLE)|tojson}}, {% endfor %}\n      ]\n    },\n    options: {\n      onClick (evt, item) {\n        if (!item[0]) return;\n        var value = this.data.labels[item[0]._index].replace({{NO_TITLE|tojson}}, \"\");\n        var form = $('<form></form>');\n\n        form.attr(\"method\", \"get\");\n        form.attr(\"action\", \"{{url_for('bookmark.index_view')}}\");\n\n        var field = $('<input></input>');\n\n        field.attr(\"type\", \"hidden\");\n        field.attr(\"name\", \"flt0_title_equals\");\n        field.attr(\"value\", value);\n        form.append(field);\n\n        // The form needs to be a part of the document in\n        // order for us to be able to submit it.\n        $(document.body).append(form);\n        form.submit();\n      }\n    }\n  });\n  </script>\n\n</div>\n{% endblock %}\n"
  },
  {
    "path": "bukuserver/templates/bukuserver/tag_edit.html",
    "content": "{% extends 'admin/model/edit.html' %}\n{% import 'bukuserver/lib.html' as buku with context %}\n\n{% block tail %}\n  {{ super() }}\n  {{ buku.set_lang() }}\n  {{ buku.brand_dbname() }}\n  {{ buku.horizontal_form() }}\n  {{ buku.focus() }}\n{% endblock %}\n"
  },
  {
    "path": "bukuserver/templates/bukuserver/tags_list.html",
    "content": "{% extends 'admin/model/list.html' %}\n{% import 'bukuserver/lib.html' as buku with context %}\n\n{% block head %}\n  {{ super() }}\n  {{ buku.close_if_popup() }}\n  {{ buku.brand_dbname() }}\n{% endblock %}\n\n{% block model_menu_bar_before_filters %}\n  {{ super() }}\n  <form id=\"refresh\" class=\"d-none\" method=\"POST\" action=\"refresh\"></form>\n  <li class=\"nav-item\">\n    <a class=\"nav-link\" href=\"#\" onclick=\"refresh.submit()\">{{ _('Refresh') }}</a>\n  </li>\n{% endblock %}\n\n{% block tail %}\n  {{ buku.fix_translations('tags') }}\n  {{ super() }}\n  {{ buku.page_size_custom() }}\n  {{ buku.focus(None) }}\n  <script>$('tr:has(a[href$=\"/bookmark/?flt0_tags_number_equal=0\"]) .list-buttons-column').html('')</script>\n{% endblock %}\n"
  },
  {
    "path": "bukuserver/translations/.gitignore",
    "content": "/messages.pot\n"
  },
  {
    "path": "bukuserver/translations/README.md",
    "content": "## Bukuserver translations\n\nThis directory contains translations activated by installing Flask-Babel(Ex) and providing `BUKUSERVER_LOCALE` environment variable.\n(If a locale is missing or incomplete, the original \"keys\" are used as translations; also `_gettext` converts strings using Flask-Admin translation files.)\n\nThe `__init__.py` file includes wrapper scripts for Babel CLI interface; a simple CLI is provided in `__main__.py`.\n\nTranslation files can be found as `<locale>/LC_MESSAGES/messages.po` (their corresponding _compiled_ versions have a `.mo` extension);\ntheir layout is generated by collecting translation keys from Python/Jinja sources, and appending the `messages_custom.pot` contents\n(for keys that can't be detected automatically).\n\n### Script\n\nThe `__main__.py` file contains a script that does the following:\n1. collect translation keys from Python and Jinja invocations of `gettext()`/`lazy_gettext()`/`pgettext()` +\n   shorthands `_()`/`_l()`/`_p()`/`_lp()`, and generate a \"template\" translation file `messages.pot`\n2. patch said \"template\" with initial values (most importantly a hardcoded creation date, which prevents \"changes\" from appearing in unchanged translations)\n   and append contents of `messages_custom.pot` to it\n3. update existing translation files (`*/LC_MESSAGES/messages.po`) to match the new \"template\" (note that if you've removed/renamed some translation keys,\n   their current values will be commented out and moved to the bottom; so you'll need to edit _all_ translation files after that)\n4. if any locale names are supplied, corresponding locale files are created (or updated if they exist already)\n5. compile all translation files into matching `.mo` binaries\n\nSteps 1-2 are implemented as `translations_generate()`, 3-4 as `translations_update()`, and 5 as `translations_compile()` (in the `__init__.py` file).\n\n### Usage\n\n```sh\npython .\n```\nWill run the `__main__.py` script (if running from a different folder, pass relative path to it instead of `.`)\n```sh\npython -m bukuserver.translations\n```\nHas the same effect _when run **in a virtualenv**_ in which `buku` was _installed as an **`--editable`** package_.\n\nRun the script in following events:\n* after you've added/removed/renamed some translation keys in the source (note that in the latter two cases you'll need to _edit all translation files_,\n  to either remove or restore commented out translations of keys no longer found in `messages.pot`; _alternatively_ you can edit these keys before running the script)\n* after you've edited some translation file(s), to compile them\n* when you want to add one or more new locales (provide their codes as additional parameters to the script)\n\nAdditionally, providing `-h` or `--help` will cause brief usage info to be printed out instead.\n"
  },
  {
    "path": "bukuserver/translations/__init__.py",
    "content": "import os\nimport re\nfrom babel.messages.frontend import CommandLineInterface as pybabel\ntry:\n    from buku import __version__\nexcept ImportError:\n    __version__ = None\n\nDOMAIN = 'messages'\nDIR = os.path.dirname(__file__)\nBUKUSERVER = os.path.dirname(DIR)\nMAPPING = os.path.join(DIR, 'babel.cfg')\nTEMPLATE = os.path.join(DIR, 'messages.pot')\nCUSTOM = os.path.join(DIR, 'messages_custom.pot')\n\n_EOL, _STR_EOL, _G_STR_EOL = r'$\\r?\\n?', r'\"[^\\r\\n]*\"$\\r?\\n?', r'\"([^\\r\\n]*)\"$\\r?\\n?'\nSTRINGS = {\n    r'\\bPROJECT VERSION\\b': __version__ or '???',\n    r'(?<=for )PROJECT\\b': 'bukuserver',\n    r'(?<=as the )PROJECT(?= project)': 'buku',\n    r'\\bORGANIZATION\\b': 'buku',\n    f'^# FIRST AUTHOR <EMAIL@ADDRESS>, [0-9]+.{_EOL}': '',\n    f'^#, fuzzy{_EOL}': '',\n    r'(?<=^\"POT-Creation-Date: ).*(?=\\\\n\"$)': '2024-09-12 00:00+0000',  # avoid git updates of unchanged translations\n}\nOLD_BLANK = re.compile(f'^(?:#~ msgctxt {_STR_EOL})?#~ msgid {_STR_EOL}#~ msgstr \"\"{_EOL}(?:{_EOL})?', re.MULTILINE)\nOLD = re.compile(f'^(?:#~ msgctxt {_G_STR_EOL})?#~ msgid {_G_STR_EOL}#~ msgstr {_G_STR_EOL}(?:{_EOL})?', re.MULTILINE)\n\ndef replace_obsolete(text):\n    '''Removes *blank* obsolete entries, and restores re-added *blank* values from old obsolete ones'''\n    text = re.sub(OLD_BLANK, '', text)\n    for obsolete in re.finditer(OLD, text):\n        _ctxt, _id, _str = obsolete.groups()\n        ctxt_re = ('' if _ctxt is None else f'msgctxt \"{re.escape(_ctxt)}\"{_EOL}')\n        if m := re.search(f'^{ctxt_re}msgid \"{re.escape(_id)}\"{_EOL}msgstr \"()\"{_EOL}', text, re.MULTILINE):\n            text = (text[:m.start(1)] + _str + text[m.end(1):]).replace(obsolete.group(0), '', 1)\n    return text\n\ndef translations_generate():\n    '''Generates and patches the messages.pot template file'''\n    pybabel().run(['', 'extract', '--no-wrap', f'--mapping-file={MAPPING}',\n                   '--keywords=_ _l _p:1c,2 _lp:1c,2 lazy_gettext', f'--output-file={TEMPLATE}', BUKUSERVER])\n    print(f'patching PO template file at {TEMPLATE}')\n    with open(TEMPLATE, encoding='utf-8') as fin:\n        text = fin.read()\n    for k, v in STRINGS.items():\n        text = re.sub(k, v, text, count=1, flags=re.MULTILINE)\n    with open(CUSTOM, encoding='utf-8') as fin:\n        with open(TEMPLATE, 'w', encoding='utf-8') as fout:\n            fout.write(text + fin.read())\n\ndef translations_update(new_locales=[], generate=True, domain=DOMAIN, fuzzy=False):\n    '''Updates all existing translations (*/LC_MESSAGES/messages.po) based on messages.pot'''\n    generate and translations_generate()\n    command = (['', 'update', '--no-wrap'] + ([] if fuzzy else ['--no-fuzzy-matching']) +\n               [f'--domain={domain}', f'--input-file={TEMPLATE}', f'--output-dir={DIR}'])\n    pybabel().run(command)\n    for locale in new_locales:\n        try:  # trying an update first, to prevent clearing an existing translation\n            pybabel().run(command + ['--init-missing', f'--locale={locale}'])\n        except FileNotFoundError:\n            pybabel().run(['', 'init', '--no-wrap', f'--domain={domain}', f'--input-file={TEMPLATE}',\n                           f'--output-dir={DIR}', f'--locale={locale}'])\n    # handling obsolete entries (removing blank and restoring re-added keys)\n    for locale in os.listdir(DIR):\n        filename = os.path.join(DIR, locale, 'LC_MESSAGES', f'{domain}.po')\n        if os.path.isfile(filename) and os.access(filename, os.W_OK):\n            with open(filename, encoding='utf-8') as fin:\n                text = fin.read()\n            if text != (stripped := replace_obsolete(text)):\n                print(f'processing obsolete entries from catalog {filename}')\n                with open(filename, 'w', encoding='utf-8') as fout:\n                    fout.write(stripped)\n\ndef translations_compile(update=False, generate=True, domain=DOMAIN, new_locales=[], fuzzy=False):\n    '''Compiles all existing translations'''\n    update and translations_update(generate=generate, domain=DOMAIN, new_locales=new_locales, fuzzy=fuzzy)\n    pybabel().run(['', 'compile', f'--domain={domain}', f'--directory={DIR}'])\n"
  },
  {
    "path": "bukuserver/translations/__main__.py",
    "content": "#!/usr/bin/env python\nimport os\nimport sys\n\ntry:\n    from . import translations_compile, __version__\nexcept ImportError:\n    from bukuserver.translations import translations_compile, __version__\n\nif __name__ == '__main__':\n    if any(s in sys.argv[1:] for s in ['-h', '--help']):\n        print(f'  Usage: python {sys.argv[0]} [new-locale [...]]')\n        print('    This script updates Bukuserver translation files (and/or adds new locales)')\n        print('    FUZZY=yes (or any other non-empty value) enables fuzzy matching')\n        print(f'  [buku version: {__version__ or \"???\"}]')\n    else:\n        new_locales = [s for s in sys.argv[1:] if s[:1] not in ('', '-')]\n        translations_compile(update=True, new_locales=new_locales, fuzzy=bool(os.environ.get('FUZZY')))\n"
  },
  {
    "path": "bukuserver/translations/babel.cfg",
    "content": "[python: **.py]\n[jinja2: **/templates/**.html]\n"
  },
  {
    "path": "bukuserver/translations/de/LC_MESSAGES/messages.po",
    "content": "# German translations for bukuserver.\n# Copyright (C) 2024 buku\n# This file is distributed under the same license as the buku project.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version:  4.9\\n\"\n\"Report-Msgid-Bugs-To: EMAIL@ADDRESS\\n\"\n\"POT-Creation-Date: 2024-09-12 00:00+0000\\n\"\n\"PO-Revision-Date: 2024-09-08 19:42+0200\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language: de\\n\"\n\"Language-Team: de <LL@li.org>\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.17.0\\n\"\n\n#: /home/lex/Work/buku/bukuserver/api.py:272\nmsgid \"Input required.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:61\nmsgid \"equals\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:62\nmsgid \"not equals\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:63\nmsgid \"contains\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:64\nmsgid \"not contains\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:65\nmsgid \"greater than\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:66\nmsgid \"smaller than\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:67\nmsgid \"in list\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:68\nmsgid \"not in list\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:69\nmsgid \"top X\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:70\nmsgid \"bottom X\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:124\nmsgid \"natural\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:124\nmsgid \"reversed\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:21\nmsgid \"The value must be a string.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:49\nmsgid \"Invalid input.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:53\nmsgid \"Keywords\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:54\nmsgid \"Match all keywords\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:54\nmsgid \"Exclude partial matches (with multiple keywords)\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:55\nmsgid \"With markers\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:56\nmsgid \"The search string will be split into multiple keywords, each will be applied to a field based on prefix:\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:57\nmsgid \" - keywords starting with '.', '>' or ':' will be searched for in title, description and URL respectively\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:58\nmsgid \" - '#' will be searched for in tags (comma-separated, partial matches; not affected by Deep Search)\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:59\nmsgid \" - '#,' is the same but will match FULL tags only\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:60\nmsgid \" - '*' will be searched for in all fields (this prefix can be omitted in the 1st keyword)\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:61\nmsgid \"Keywords need to be separated by placing spaces before the prefix.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:63\nmsgid \"Deep search\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:63\nmsgid \"When unset, only FULL words will be matched.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:64\nmsgid \"Regex\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:64\nmsgid \"The keyword(s) are regular expressions (overrides other options).\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:68\nmsgid \"Keyword\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:72\n#: /home/lex/Work/buku/bukuserver/views.py:174\nmsgid \"URL\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:73\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:190\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:225\n#: /home/lex/Work/buku/bukuserver/views.py:175\nmsgid \"Title\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:74\n#: /home/lex/Work/buku/bukuserver/server.py:150\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:31\n#: /home/lex/Work/buku/bukuserver/views.py:175\nmsgid \"Tags\"\nmsgstr \"Schilder\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:75\n#: /home/lex/Work/buku/bukuserver/views.py:175\nmsgid \"Description\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:121\nmsgid \"Delete tags list from existing tags\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/server.py:149\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:30\nmsgid \"Bookmarks\"\nmsgstr \"Lesezeichen\"\n\n#: /home/lex/Work/buku/bukuserver/server.py:151\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:32\nmsgid \"Statistic\"\nmsgstr \"Statistik\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:112\nmsgid \"Duplicate URL\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:113\nmsgid \"Rejected by the database\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:126\nmsgid \"<EMPTY TITLE>\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:174\nmsgid \"Entry\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:174\nmsgid \"Index\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:223\n#, python-format\nmsgid \"url invalid: %(url)s\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:234\nmsgid \"Failed to create record.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:247\n#: /home/lex/Work/buku/bukuserver/views.py:562\nmsgid \"Failed to delete record.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:262\nmsgid \"Invalid search mode combination\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:278\nmsgid \"Reordered bookmarks in DB\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:358\nmsgid \"netloc match\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:391\nmsgid \"contain\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:392\nmsgid \"not contain\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:393\nmsgid \"number equal\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:394\nmsgid \"number not equal\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:395\nmsgid \"number greater than\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:396\nmsgid \"number smaller than\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:422\n#: /home/lex/Work/buku/bukuserver/views.py:580\nmsgid \"Failed to update record.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:437\nmsgid \"<UNTAGGED>\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:443\n#: /home/lex/Work/buku/bukuserver/views.py:480\nmsgctxt \"tag\"\nmsgid \"Name\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:443\nmsgctxt \"tag\"\nmsgid \"Usage Count\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:543\nmsgid \"top most common\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmark_details_modal.html:7\nmsgid \"Pick another\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:10\n#, python-brace-format\nmsgid \"Swap record #{} with record #\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:12\n#, python-brace-format\nmsgid \"Not a valid record index: '{}'\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:13\n#, python-brace-format\nmsgid \"There are only {} records in total!\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:14\nmsgid \"Swapping a record with itself has no effect!\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:25\nmsgid \"Random\"\nmsgstr \"Zufälliger\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:31\nmsgid \"Update indices to match this order\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:32\nmsgid \"Reorder\"\nmsgstr \"Neu anordnen\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:35\nmsgid \"Save this order in DB?\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:58\nmsgid \"Swap with…\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:58\nmsgid \"Move down\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:58\nmsgid \"Move up\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:15\nmsgid \"Search bookmark\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:28\nmsgid \"Bookmark manager like a text-based mini-web\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:49\nmsgid \"Bookmarklet\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:51\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:67\nmsgid \"Add to Buku\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:53\nmsgid \"Note: if you select text on the page before activating the bookmarklet, it'll be used as description instead of page metadata.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:58\nmsgid \"Location Bar (keyboard-only) shortcut\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:60\nmsgid \"in Firefox:\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:61\n#, python-format\nmsgid \"Open the bookmarks editor and set %(buku)s in the Keyword field of the bookmarklet.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:63\nmsgid \"in Chrome:\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:65\n#, python-format\nmsgid \"In %(path)s, add a new row by placing %(add_to_buku)s, %(buku)s, and the copied bookmarklet URL in respective fields).\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:66\nmsgid \"Settings > Search engine > Manage… > Site Search\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:70\nmsgid \"usage:\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:72\n#, python-format\nmsgid \"By hitting %(hotkey)s (thus switching to Location Bar), then typing %(buku)s and hitting %(enter)s, you'll be able to open the bookmarklet dialog via keyboard only.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:75\n#, python-format\nmsgid \"Note: in Firefox this changes displayed URL, but you can reset it by switching back to Location Bar and hitting %(escape)s twice.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:89\nmsgid \"FULL\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:64\nmsgid \"Collect missing data (+extra tags) by fetching & parsing the webpage\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:67\nmsgid \"Fetch\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:97\nmsgctxt \"tags\"\nmsgid \"name\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:97\nmsgctxt \"tags\"\nmsgid \"usage count\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:98\nmsgctxt \"bookmarks\"\nmsgid \"index\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:98\nmsgctxt \"bookmarks\"\nmsgid \"url\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:98\nmsgctxt \"bookmarks\"\nmsgid \"title\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:99\nmsgctxt \"bookmarks\"\nmsgid \"tags\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:99\nmsgctxt \"bookmarks\"\nmsgid \"order\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:101\nmsgid \"Delete record\"\nmsgstr \"Datenzatz löschen\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:131\nmsgid \"custom\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:132\nmsgid \"Set custom page size (empty for default)\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:138\nmsgid \"Invalid page size\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:9\n#, python-brace-format\nmsgid \"{} days ago\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:9\n#, python-brace-format\nmsgid \"{} hours ago\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:9\n#, python-brace-format\nmsgid \"{} minutes ago\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:9\n#, python-brace-format\nmsgid \"{} seconds ago\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:13\nmsgid \"just now\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:22\nmsgid \"Data created\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:24\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/tags_list.html:13\nmsgid \"Refresh\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:26\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:44\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:77\nmsgid \"Netloc\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:37\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:110\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:183\nmsgid \"View all\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:43\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:76\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:116\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:151\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:189\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:224\nmsgid \"Rank\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:45\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:78\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:118\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:153\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:191\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:226\nmsgid \"Number\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:52\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:86\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:258\nmsgid \"(no netloc)\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:61\nmsgid \"No bookmarks found.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:70\nmsgid \"Netloc ranking\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:99\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:117\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:152\nmsgid \"Tag\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:136\nmsgid \"No tags found.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:145\nmsgid \"Tag ranking\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:172\nmsgid \"Title (common)\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:199\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:234\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:327\nmsgid \"(no title)\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:209\nmsgid \"No common titles found.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:218\nmsgid \"Common titles ranking\"\nmsgstr \"\"\n\nmsgid \"Original and replacement tags are the same.\"\nmsgstr \"\"\n\nmsgid \"Tag name cannot be blank.\"\nmsgstr \"\"\n\nmsgid \"by index\"\nmsgstr \"\"\n\nmsgid \"by url\"\nmsgstr \"\"\n\nmsgid \"by netloc\"\nmsgstr \"\"\n\nmsgid \"by title\"\nmsgstr \"\"\n\nmsgid \"by description\"\nmsgstr \"\"\n\nmsgid \"by tags\"\nmsgstr \"\"\n\nmsgid \"with tag first\"\nmsgstr \"\"\n\nmsgid \"with tag last\"\nmsgstr \"\"\n\nmsgid \"search\"\nmsgstr \"\"\n\nmsgid \"search regex\"\nmsgstr \"\"\n\nmsgid \"search deep\"\nmsgstr \"\"\n\nmsgid \"search match all\"\nmsgstr \"\"\n\nmsgid \"search match all, deep\"\nmsgstr \"\"\n\nmsgid \"search markers\"\nmsgstr \"\"\n\nmsgid \"search markers, regex\"\nmsgstr \"\"\n\nmsgid \"search markers, deep\"\nmsgstr \"\"\n\nmsgid \"search markers, match all\"\nmsgstr \"\"\n\nmsgid \"search markers, match all, deep\"\nmsgstr \"\"\n\n"
  },
  {
    "path": "bukuserver/translations/fr/LC_MESSAGES/messages.po",
    "content": "# French translations for bukuserver.\n# Copyright (C) 2024 buku\n# This file is distributed under the same license as the buku project.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version:  4.9\\n\"\n\"Report-Msgid-Bugs-To: EMAIL@ADDRESS\\n\"\n\"POT-Creation-Date: 2024-09-12 00:00+0000\\n\"\n\"PO-Revision-Date: 2024-09-08 19:42+0200\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language: fr\\n\"\n\"Language-Team: fr <LL@li.org>\\n\"\n\"Plural-Forms: nplurals=2; plural=(n > 1);\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.17.0\\n\"\n\n#: /home/lex/Work/buku/bukuserver/api.py:272\nmsgid \"Input required.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:61\nmsgid \"equals\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:62\nmsgid \"not equals\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:63\nmsgid \"contains\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:64\nmsgid \"not contains\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:65\nmsgid \"greater than\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:66\nmsgid \"smaller than\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:67\nmsgid \"in list\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:68\nmsgid \"not in list\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:69\nmsgid \"top X\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:70\nmsgid \"bottom X\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:124\nmsgid \"natural\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:124\nmsgid \"reversed\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:21\nmsgid \"The value must be a string.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:49\nmsgid \"Invalid input.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:53\nmsgid \"Keywords\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:54\nmsgid \"Match all keywords\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:54\nmsgid \"Exclude partial matches (with multiple keywords)\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:55\nmsgid \"With markers\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:56\nmsgid \"The search string will be split into multiple keywords, each will be applied to a field based on prefix:\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:57\nmsgid \" - keywords starting with '.', '>' or ':' will be searched for in title, description and URL respectively\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:58\nmsgid \" - '#' will be searched for in tags (comma-separated, partial matches; not affected by Deep Search)\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:59\nmsgid \" - '#,' is the same but will match FULL tags only\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:60\nmsgid \" - '*' will be searched for in all fields (this prefix can be omitted in the 1st keyword)\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:61\nmsgid \"Keywords need to be separated by placing spaces before the prefix.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:63\nmsgid \"Deep search\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:63\nmsgid \"When unset, only FULL words will be matched.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:64\nmsgid \"Regex\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:64\nmsgid \"The keyword(s) are regular expressions (overrides other options).\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:68\nmsgid \"Keyword\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:72\n#: /home/lex/Work/buku/bukuserver/views.py:174\nmsgid \"URL\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:73\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:190\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:225\n#: /home/lex/Work/buku/bukuserver/views.py:175\nmsgid \"Title\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:74\n#: /home/lex/Work/buku/bukuserver/server.py:150\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:31\n#: /home/lex/Work/buku/bukuserver/views.py:175\nmsgid \"Tags\"\nmsgstr \"Étiquettes\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:75\n#: /home/lex/Work/buku/bukuserver/views.py:175\nmsgid \"Description\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:121\nmsgid \"Delete tags list from existing tags\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/server.py:149\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:30\nmsgid \"Bookmarks\"\nmsgstr \"Signets\"\n\n#: /home/lex/Work/buku/bukuserver/server.py:151\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:32\nmsgid \"Statistic\"\nmsgstr \"Statistique\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:112\nmsgid \"Duplicate URL\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:113\nmsgid \"Rejected by the database\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:126\nmsgid \"<EMPTY TITLE>\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:174\nmsgid \"Entry\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:174\nmsgid \"Index\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:223\n#, python-format\nmsgid \"url invalid: %(url)s\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:234\nmsgid \"Failed to create record.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:247\n#: /home/lex/Work/buku/bukuserver/views.py:562\nmsgid \"Failed to delete record.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:262\nmsgid \"Invalid search mode combination\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:278\nmsgid \"Reordered bookmarks in DB\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:358\nmsgid \"netloc match\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:391\nmsgid \"contain\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:392\nmsgid \"not contain\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:393\nmsgid \"number equal\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:394\nmsgid \"number not equal\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:395\nmsgid \"number greater than\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:396\nmsgid \"number smaller than\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:422\n#: /home/lex/Work/buku/bukuserver/views.py:580\nmsgid \"Failed to update record.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:437\nmsgid \"<UNTAGGED>\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:443\n#: /home/lex/Work/buku/bukuserver/views.py:480\nmsgctxt \"tag\"\nmsgid \"Name\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:443\nmsgctxt \"tag\"\nmsgid \"Usage Count\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:543\nmsgid \"top most common\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmark_details_modal.html:7\nmsgid \"Pick another\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:10\n#, python-brace-format\nmsgid \"Swap record #{} with record #\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:12\n#, python-brace-format\nmsgid \"Not a valid record index: '{}'\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:13\n#, python-brace-format\nmsgid \"There are only {} records in total!\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:14\nmsgid \"Swapping a record with itself has no effect!\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:25\nmsgid \"Random\"\nmsgstr \"Aléatoire\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:31\nmsgid \"Update indices to match this order\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:32\nmsgid \"Reorder\"\nmsgstr \"Réorganiser\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:35\nmsgid \"Save this order in DB?\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:58\nmsgid \"Swap with…\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:58\nmsgid \"Move down\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:58\nmsgid \"Move up\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:15\nmsgid \"Search bookmark\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:28\nmsgid \"Bookmark manager like a text-based mini-web\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:49\nmsgid \"Bookmarklet\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:51\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:67\nmsgid \"Add to Buku\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:53\nmsgid \"Note: if you select text on the page before activating the bookmarklet, it'll be used as description instead of page metadata.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:58\nmsgid \"Location Bar (keyboard-only) shortcut\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:60\nmsgid \"in Firefox:\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:61\n#, python-format\nmsgid \"Open the bookmarks editor and set %(buku)s in the Keyword field of the bookmarklet.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:63\nmsgid \"in Chrome:\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:65\n#, python-format\nmsgid \"In %(path)s, add a new row by placing %(add_to_buku)s, %(buku)s, and the copied bookmarklet URL in respective fields).\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:66\nmsgid \"Settings > Search engine > Manage… > Site Search\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:70\nmsgid \"usage:\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:72\n#, python-format\nmsgid \"By hitting %(hotkey)s (thus switching to Location Bar), then typing %(buku)s and hitting %(enter)s, you'll be able to open the bookmarklet dialog via keyboard only.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:75\n#, python-format\nmsgid \"Note: in Firefox this changes displayed URL, but you can reset it by switching back to Location Bar and hitting %(escape)s twice.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:89\nmsgid \"FULL\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:64\nmsgid \"Collect missing data (+extra tags) by fetching & parsing the webpage\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:67\nmsgid \"Fetch\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:97\nmsgctxt \"tags\"\nmsgid \"name\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:97\nmsgctxt \"tags\"\nmsgid \"usage count\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:98\nmsgctxt \"bookmarks\"\nmsgid \"index\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:98\nmsgctxt \"bookmarks\"\nmsgid \"url\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:98\nmsgctxt \"bookmarks\"\nmsgid \"title\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:99\nmsgctxt \"bookmarks\"\nmsgid \"tags\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:99\nmsgctxt \"bookmarks\"\nmsgid \"order\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:101\nmsgid \"Delete record\"\nmsgstr \"Supprimer l'enregistrement\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:131\nmsgid \"custom\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:132\nmsgid \"Set custom page size (empty for default)\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:138\nmsgid \"Invalid page size\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:9\n#, python-brace-format\nmsgid \"{} days ago\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:9\n#, python-brace-format\nmsgid \"{} hours ago\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:9\n#, python-brace-format\nmsgid \"{} minutes ago\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:9\n#, python-brace-format\nmsgid \"{} seconds ago\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:13\nmsgid \"just now\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:22\nmsgid \"Data created\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:24\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/tags_list.html:13\nmsgid \"Refresh\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:26\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:44\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:77\nmsgid \"Netloc\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:37\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:110\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:183\nmsgid \"View all\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:43\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:76\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:116\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:151\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:189\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:224\nmsgid \"Rank\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:45\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:78\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:118\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:153\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:191\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:226\nmsgid \"Number\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:52\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:86\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:258\nmsgid \"(no netloc)\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:61\nmsgid \"No bookmarks found.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:70\nmsgid \"Netloc ranking\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:99\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:117\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:152\nmsgid \"Tag\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:136\nmsgid \"No tags found.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:145\nmsgid \"Tag ranking\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:172\nmsgid \"Title (common)\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:199\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:234\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:327\nmsgid \"(no title)\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:209\nmsgid \"No common titles found.\"\nmsgstr \"\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:218\nmsgid \"Common titles ranking\"\nmsgstr \"\"\n\nmsgid \"Original and replacement tags are the same.\"\nmsgstr \"\"\n\nmsgid \"Tag name cannot be blank.\"\nmsgstr \"\"\n\nmsgid \"by index\"\nmsgstr \"\"\n\nmsgid \"by url\"\nmsgstr \"\"\n\nmsgid \"by netloc\"\nmsgstr \"\"\n\nmsgid \"by title\"\nmsgstr \"\"\n\nmsgid \"by description\"\nmsgstr \"\"\n\nmsgid \"by tags\"\nmsgstr \"\"\n\nmsgid \"with tag first\"\nmsgstr \"\"\n\nmsgid \"with tag last\"\nmsgstr \"\"\n\nmsgid \"search\"\nmsgstr \"\"\n\nmsgid \"search regex\"\nmsgstr \"\"\n\nmsgid \"search deep\"\nmsgstr \"\"\n\nmsgid \"search match all\"\nmsgstr \"\"\n\nmsgid \"search match all, deep\"\nmsgstr \"\"\n\nmsgid \"search markers\"\nmsgstr \"\"\n\nmsgid \"search markers, regex\"\nmsgstr \"\"\n\nmsgid \"search markers, deep\"\nmsgstr \"\"\n\nmsgid \"search markers, match all\"\nmsgstr \"\"\n\nmsgid \"search markers, match all, deep\"\nmsgstr \"\"\n\n"
  },
  {
    "path": "bukuserver/translations/messages_custom.pot",
    "content": "msgid \"Original and replacement tags are the same.\"\nmsgstr \"\"\n\nmsgid \"Tag name cannot be blank.\"\nmsgstr \"\"\n\nmsgid \"by index\"\nmsgstr \"\"\n\nmsgid \"by url\"\nmsgstr \"\"\n\nmsgid \"by netloc\"\nmsgstr \"\"\n\nmsgid \"by title\"\nmsgstr \"\"\n\nmsgid \"by description\"\nmsgstr \"\"\n\nmsgid \"by tags\"\nmsgstr \"\"\n\nmsgid \"with tag first\"\nmsgstr \"\"\n\nmsgid \"with tag last\"\nmsgstr \"\"\n\nmsgid \"search\"\nmsgstr \"\"\n\nmsgid \"search regex\"\nmsgstr \"\"\n\nmsgid \"search deep\"\nmsgstr \"\"\n\nmsgid \"search match all\"\nmsgstr \"\"\n\nmsgid \"search match all, deep\"\nmsgstr \"\"\n\nmsgid \"search markers\"\nmsgstr \"\"\n\nmsgid \"search markers, regex\"\nmsgstr \"\"\n\nmsgid \"search markers, deep\"\nmsgstr \"\"\n\nmsgid \"search markers, deep\"\nmsgstr \"\"\n\nmsgid \"search markers, match all\"\nmsgstr \"\"\n\nmsgid \"search markers, match all, deep\"\nmsgstr \"\"\n"
  },
  {
    "path": "bukuserver/translations/ru/LC_MESSAGES/messages.po",
    "content": "# Russian translations for bukuserver.\n# Copyright (C) 2024 buku\n# This file is distributed under the same license as the buku project.\n#\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version:  4.9\\n\"\n\"Report-Msgid-Bugs-To: EMAIL@ADDRESS\\n\"\n\"POT-Creation-Date: 2024-09-12 00:00+0000\\n\"\n\"PO-Revision-Date: 2024-09-08 19:42+0200\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language: ru\\n\"\n\"Language-Team: ru <LL@li.org>\\n\"\n\"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=utf-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Generated-By: Babel 2.17.0\\n\"\n\n#: /home/lex/Work/buku/bukuserver/api.py:272\nmsgid \"Input required.\"\nmsgstr \"Введите данные.\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:61\nmsgid \"equals\"\nmsgstr \"равняется\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:62\nmsgid \"not equals\"\nmsgstr \"не равняется\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:63\nmsgid \"contains\"\nmsgstr \"содержит\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:64\nmsgid \"not contains\"\nmsgstr \"не содержит\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:65\nmsgid \"greater than\"\nmsgstr \"больше чем\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:66\nmsgid \"smaller than\"\nmsgstr \"меньше чем\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:67\nmsgid \"in list\"\nmsgstr \"в списке\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:68\nmsgid \"not in list\"\nmsgstr \"не в списке\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:69\nmsgid \"top X\"\nmsgstr \"первые X\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:70\nmsgid \"bottom X\"\nmsgstr \"последние X\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:124\nmsgid \"natural\"\nmsgstr \"естественный\"\n\n#: /home/lex/Work/buku/bukuserver/filters.py:124\nmsgid \"reversed\"\nmsgstr \"обратный\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:21\nmsgid \"The value must be a string.\"\nmsgstr \"Значение должно быть строкой.\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:49\nmsgid \"Invalid input.\"\nmsgstr \"Некорректный ввод\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:53\nmsgid \"Keywords\"\nmsgstr \"Ключевые слова\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:54\nmsgid \"Match all keywords\"\nmsgstr \"Все подстроки должны совпасть\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:54\nmsgid \"Exclude partial matches (with multiple keywords)\"\nmsgstr \"Исключить частичные совпадения (при поиске более одной подстроки)\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:55\nmsgid \"With markers\"\nmsgstr \"С маркерами\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:56\nmsgid \"The search string will be split into multiple keywords, each will be applied to a field based on prefix:\"\nmsgstr \"Строка поиска разбивается на подстроки, применяемые к полям в зависимости от префикса:\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:57\nmsgid \" - keywords starting with '.', '>' or ':' will be searched for in title, description and URL respectively\"\nmsgstr \" - подстроки начинающиеся с '.', '>' или ':' ищутся в названии, описании и ссылке соответственно\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:58\nmsgid \" - '#' will be searched for in tags (comma-separated, partial matches; not affected by Deep Search)\"\nmsgstr \" - '#' ищется в тегах (через запятую, частичные совпадения; «глубокий поиск» игнорируется)\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:59\nmsgid \" - '#,' is the same but will match FULL tags only\"\nmsgstr \" - '#,' работает так же но проверяет теги на ПОЛНОЕ совпадение\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:60\nmsgid \" - '*' will be searched for in all fields (this prefix can be omitted in the 1st keyword)\"\nmsgstr \" - '*' будет искаться во всех полях (этот префикс можно опустить в 1-й подстроке)\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:61\nmsgid \"Keywords need to be separated by placing spaces before the prefix.\"\nmsgstr \"Подстроки нужно разделять пробелом перед префиксом.\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:63\nmsgid \"Deep search\"\nmsgstr \"Глубокий поиск\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:63\nmsgid \"When unset, only FULL words will be matched.\"\nmsgstr \"Если это не выбрано, слова проверяются только на ПОЛНОЕ совпадение.\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:64\nmsgid \"Regex\"\nmsgstr \"Регулярное выражение\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:64\nmsgid \"The keyword(s) are regular expressions (overrides other options).\"\nmsgstr \"Искомая подстрока(-и) – регулярное выражение (замещает остальные параметры).\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:68\nmsgid \"Keyword\"\nmsgstr \"Искать\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:72\n#: /home/lex/Work/buku/bukuserver/views.py:174\nmsgid \"URL\"\nmsgstr \"Ссылка\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:73\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:190\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:225\n#: /home/lex/Work/buku/bukuserver/views.py:175\nmsgid \"Title\"\nmsgstr \"Название\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:74\n#: /home/lex/Work/buku/bukuserver/server.py:150\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:31\n#: /home/lex/Work/buku/bukuserver/views.py:175\nmsgid \"Tags\"\nmsgstr \"Теги\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:75\n#: /home/lex/Work/buku/bukuserver/views.py:175\nmsgid \"Description\"\nmsgstr \"Описание\"\n\n#: /home/lex/Work/buku/bukuserver/forms.py:121\nmsgid \"Delete tags list from existing tags\"\nmsgstr \"Удалить список тегов из существующих\"\n\n#: /home/lex/Work/buku/bukuserver/server.py:149\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:30\nmsgid \"Bookmarks\"\nmsgstr \"Закладки\"\n\n#: /home/lex/Work/buku/bukuserver/server.py:151\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:32\nmsgid \"Statistic\"\nmsgstr \"Статистика\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:112\nmsgid \"Duplicate URL\"\nmsgstr \"Такая ссылка в базе уже есть\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:113\nmsgid \"Rejected by the database\"\nmsgstr \"База данных отказала в выполнении операции\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:126\nmsgid \"<EMPTY TITLE>\"\nmsgstr \"<БЕЗ НАЗВАНИЯ>\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:174\nmsgid \"Entry\"\nmsgstr \"Закладка\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:174\nmsgid \"Index\"\nmsgstr \"Номер\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:223\n#, python-format\nmsgid \"url invalid: %(url)s\"\nmsgstr \"некорректная ссылка: %(url)s\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:234\nmsgid \"Failed to create record.\"\nmsgstr \"Ошибка создания записи.\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:247\n#: /home/lex/Work/buku/bukuserver/views.py:562\nmsgid \"Failed to delete record.\"\nmsgstr \"Ошибка удаления записи.\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:262\nmsgid \"Invalid search mode combination\"\nmsgstr \"Некорректная комбинация фильтров поиска\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:278\nmsgid \"Reordered bookmarks in DB\"\nmsgstr \"Порядок закладок в БД был успешно изменён\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:358\nmsgid \"netloc match\"\nmsgstr \"на сайт\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:391\nmsgid \"contain\"\nmsgstr \"содержат\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:392\nmsgid \"not contain\"\nmsgstr \"не содержат\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:393\nmsgid \"number equal\"\nmsgstr \"количество равно\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:394\nmsgid \"number not equal\"\nmsgstr \"количество не равно\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:395\nmsgid \"number greater than\"\nmsgstr \"количество больше чем\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:396\nmsgid \"number smaller than\"\nmsgstr \"количество меньше чем\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:422\n#: /home/lex/Work/buku/bukuserver/views.py:580\nmsgid \"Failed to update record.\"\nmsgstr \"Ошибка обновления записи.\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:437\nmsgid \"<UNTAGGED>\"\nmsgstr \"<БЕЗ ТЕГОВ>\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:443\n#: /home/lex/Work/buku/bukuserver/views.py:480\nmsgctxt \"tag\"\nmsgid \"Name\"\nmsgstr \"Тег\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:443\nmsgctxt \"tag\"\nmsgid \"Usage Count\"\nmsgstr \"Число закладок\"\n\n#: /home/lex/Work/buku/bukuserver/views.py:543\nmsgid \"top most common\"\nmsgstr \"самое распространённое\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmark_details_modal.html:7\nmsgid \"Pick another\"\nmsgstr \"Показать другую\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:10\n#, python-brace-format\nmsgid \"Swap record #{} with record #\"\nmsgstr \"Поменять местами запись #{} с записью #\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:12\n#, python-brace-format\nmsgid \"Not a valid record index: '{}'\"\nmsgstr \"Некорректный номер строки: '{}'\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:13\n#, python-brace-format\nmsgid \"There are only {} records in total!\"\nmsgstr \"Всего существует только {} записей!\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:14\nmsgid \"Swapping a record with itself has no effect!\"\nmsgstr \"Попытка поменять запись местами саму с собой ничего не даст!\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:25\nmsgid \"Random\"\nmsgstr \"Случайная\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:31\nmsgid \"Update indices to match this order\"\nmsgstr \"Изменить порядок записей в БД на текущий\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:32\nmsgid \"Reorder\"\nmsgstr \"Изменить порядок\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:35\nmsgid \"Save this order in DB?\"\nmsgstr \"Сохранить закладки в БД в этом порядке?\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:58\nmsgid \"Swap with…\"\nmsgstr \"Поменять местами с…\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:58\nmsgid \"Move down\"\nmsgstr \"Сдвинуть вниз\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/bookmarks_list.html:58\nmsgid \"Move up\"\nmsgstr \"Сдвинуть вверх\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:15\nmsgid \"Search bookmark\"\nmsgstr \"Искать закладку\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:28\nmsgid \"Bookmark manager like a text-based mini-web\"\nmsgstr \"Менеджер закладок, как текстовая мини-сеть\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:49\nmsgid \"Bookmarklet\"\nmsgstr \"Букмарклет\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:51\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:67\nmsgid \"Add to Buku\"\nmsgstr \"Добавить в Buku\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:53\nmsgid \"Note: if you select text on the page before activating the bookmarklet, it'll be used as description instead of page metadata.\"\nmsgstr \"Заметка: если выделить текст на странице перед запуском букмарклета, он будет использован в качестве описания, вместо метаданных страницы.\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:58\nmsgid \"Location Bar (keyboard-only) shortcut\"\nmsgstr \"Ссылка для адресной панели (активация с клавиатуры)\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:60\nmsgid \"in Firefox:\"\nmsgstr \"в Firefox:\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:61\n#, python-format\nmsgid \"Open the bookmarks editor and set %(buku)s in the Keyword field of the bookmarklet.\"\nmsgstr \"Откройте редактор закладок и введите %(buku)s в поле «Ключевое слово» закладки букмарклета.\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:63\nmsgid \"in Chrome:\"\nmsgstr \"в Chrome:\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:65\n#, python-format\nmsgid \"In %(path)s, add a new row by placing %(add_to_buku)s, %(buku)s, and the copied bookmarklet URL in respective fields).\"\nmsgstr \"В меню %(path)s добавьте новую запись, введя %(add_to_buku)s, %(buku)s, и скопированную ссылку букмарклета в соответствующие поля).\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:66\nmsgid \"Settings > Search engine > Manage… > Site Search\"\nmsgstr \"Настройки > Поисковая система > Управление… > Поиск по сайту\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:70\nmsgid \"usage:\"\nmsgstr \"применение:\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:72\n#, python-format\nmsgid \"By hitting %(hotkey)s (thus switching to Location Bar), then typing %(buku)s and hitting %(enter)s, you'll be able to open the bookmarklet dialog via keyboard only.\"\nmsgstr \"Нажав %(hotkey)s (и таким образом перейдя в адресную строку), введя туда %(buku)s и нажав %(enter)s, вы можете открыть окно букмарклета используя только клавиатуру.\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:75\n#, python-format\nmsgid \"Note: in Firefox this changes displayed URL, but you can reset it by switching back to Location Bar and hitting %(escape)s twice.\"\nmsgstr \"Замечание: Firefox при этом меняет отображаемую ссылку; её можно восстановить перейдя обратно в адресную строку и нажав дважды %(escape)s.\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/home.html:89\nmsgid \"FULL\"\nmsgstr \"ПОЛНОЕ\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:64\nmsgid \"Collect missing data (+extra tags) by fetching & parsing the webpage\"\nmsgstr \"Собрать недостающие данные (+дополнительные теги) из скачанной страницы\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:67\nmsgid \"Fetch\"\nmsgstr \"Взять со страницы\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:97\nmsgctxt \"tags\"\nmsgid \"name\"\nmsgstr \"тег\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:97\nmsgctxt \"tags\"\nmsgid \"usage count\"\nmsgstr \"число закладок\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:98\nmsgctxt \"bookmarks\"\nmsgid \"index\"\nmsgstr \"номер\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:98\nmsgctxt \"bookmarks\"\nmsgid \"url\"\nmsgstr \"ссылка\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:98\nmsgctxt \"bookmarks\"\nmsgid \"title\"\nmsgstr \"название\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:99\nmsgctxt \"bookmarks\"\nmsgid \"tags\"\nmsgstr \"теги\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:99\nmsgctxt \"bookmarks\"\nmsgid \"order\"\nmsgstr \"порядок\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:101\nmsgid \"Delete record\"\nmsgstr \"Удалить запись\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:131\nmsgid \"custom\"\nmsgstr \"другой\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:132\nmsgid \"Set custom page size (empty for default)\"\nmsgstr \"Введите новый размер страницы (пустой = по умолчанию)\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/lib.html:138\nmsgid \"Invalid page size\"\nmsgstr \"Некорректный размер страницы\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:9\n#, python-brace-format\nmsgid \"{} days ago\"\nmsgstr \"{} дней назад\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:9\n#, python-brace-format\nmsgid \"{} hours ago\"\nmsgstr \"{} часов назад\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:9\n#, python-brace-format\nmsgid \"{} minutes ago\"\nmsgstr \"{} минут назад\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:9\n#, python-brace-format\nmsgid \"{} seconds ago\"\nmsgstr \"{} секунд назад\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:13\nmsgid \"just now\"\nmsgstr \"только что\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:22\nmsgid \"Data created\"\nmsgstr \"Данные созданы\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:24\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/tags_list.html:13\nmsgid \"Refresh\"\nmsgstr \"Обновить\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:26\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:44\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:77\nmsgid \"Netloc\"\nmsgstr \"Сайт\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:37\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:110\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:183\nmsgid \"View all\"\nmsgstr \"Все\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:43\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:76\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:116\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:151\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:189\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:224\nmsgid \"Rank\"\nmsgstr \"Ранг\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:45\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:78\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:118\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:153\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:191\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:226\nmsgid \"Number\"\nmsgstr \"Количество\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:52\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:86\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:258\nmsgid \"(no netloc)\"\nmsgstr \"(без сайта)\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:61\nmsgid \"No bookmarks found.\"\nmsgstr \"В базе данных нет закладок.\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:70\nmsgid \"Netloc ranking\"\nmsgstr \"Рейтинг сайтов\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:99\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:117\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:152\nmsgid \"Tag\"\nmsgstr \"Тег\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:136\nmsgid \"No tags found.\"\nmsgstr \"В базе данных нет тегов.\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:145\nmsgid \"Tag ranking\"\nmsgstr \"Рейтинг тегов\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:172\nmsgid \"Title (common)\"\nmsgstr \"Названия (повторяющиеся)\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:199\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:234\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:327\nmsgid \"(no title)\"\nmsgstr \"(без названия)\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:209\nmsgid \"No common titles found.\"\nmsgstr \"В базе данных нет закладок с повторяющимися названиями.\"\n\n#: /home/lex/Work/buku/bukuserver/templates/bukuserver/statistic.html:218\nmsgid \"Common titles ranking\"\nmsgstr \"Рейтинг повторяющихся названий\"\n\nmsgid \"Original and replacement tags are the same.\"\nmsgstr \"Для замены тега его имя нужно изменить.\"\n\nmsgid \"Tag name cannot be blank.\"\nmsgstr \"Имя тега не может быть пустым.\"\n\nmsgid \"by index\"\nmsgstr \"по номеру\"\n\nmsgid \"by url\"\nmsgstr \"по ссылке\"\n\nmsgid \"by netloc\"\nmsgstr \"по сайту\"\n\nmsgid \"by title\"\nmsgstr \"по названию\"\n\nmsgid \"by description\"\nmsgstr \"по описанию\"\n\nmsgid \"by tags\"\nmsgstr \"по тегам\"\n\nmsgid \"with tag first\"\nmsgstr \"сначала с тегом\"\n\nmsgid \"with tag last\"\nmsgstr \"сначала без тега\"\n\nmsgid \"search\"\nmsgstr \"поиск\"\n\nmsgid \"search regex\"\nmsgstr \"поиск по регулярному выражению\"\n\nmsgid \"search deep\"\nmsgstr \"глубокий поиск\"\n\nmsgid \"search match all\"\nmsgstr \"поиск всех совпадений\"\n\nmsgid \"search match all, deep\"\nmsgstr \"поиск всех совпадений, глубокий\"\n\nmsgid \"search markers\"\nmsgstr \"поиск с маркерами\"\n\nmsgid \"search markers, regex\"\nmsgstr \"поиск с маркерами, по регулярному выражению\"\n\nmsgid \"search markers, deep\"\nmsgstr \"поиск с маркерами, глубокий\"\n\nmsgid \"search markers, match all\"\nmsgstr \"поиск с маркерами, всех совпадений\"\n\nmsgid \"search markers, match all, deep\"\nmsgstr \"поиск с маркерами, всех совпадений, глубокий\"\n\n"
  },
  {
    "path": "bukuserver/util.py",
    "content": "from collections import Counter\nfrom urllib.parse import urlparse\nimport re\n\nget_netloc = lambda x: urlparse(x).netloc  # pylint: disable=no-member\nselect_filters = lambda args: {k: v for k, v in args.items() if re.match(r'flt.*_', k)}\n\nJINJA_FILTERS = {'flt': select_filters, 'netloc': get_netloc}\n\n\ndef chunks(arr, n):\n    n = max(1, n)\n    return [arr[i : i + n] for i in range(0, len(arr), n)]\n\ndef sorted_counter(keys, *, min_count=0):\n    data = Counter(keys)\n    return Counter({k: v for k, v in sorted(data.items()) if v > min_count})\n"
  },
  {
    "path": "bukuserver/views.py",
    "content": "\"\"\"views module.\"\"\"\nimport functools\nimport itertools\nimport logging\nimport random\nimport re\nimport json\nimport types\nfrom argparse import Namespace\nfrom collections import Counter, namedtuple\nfrom typing import Any, List, Optional, Tuple\nfrom urllib.parse import urlparse\n\nimport arrow\nimport wtforms\nfrom jinja2 import pass_context\nfrom flask import current_app as app, flash, redirect, request, session, url_for\nfrom flask_admin.base import AdminIndexView, BaseView, expose\nfrom flask_admin.model import BaseModelView\nfrom flask_wtf import FlaskForm\nfrom markupsafe import Markup, escape\n\nimport buku\n\ntry:\n    from . import filters as bs_filters\n    from . import forms, _, _l, _lp\n    from .filters import BookmarkField, FilterType\n    from .util import chunks, sorted_counter\nexcept ImportError:\n    from bukuserver import filters as bs_filters  # type: ignore\n    from bukuserver import forms, _, _l, _lp\n    from bukuserver.filters import BookmarkField, FilterType  # type: ignore\n    from bukuserver.util import chunks, sorted_counter\n\n\nCOLORS = ['#F7464A', '#46BFBD', '#FDB45C', '#FEDCBA', '#ABCDEF', '#DDDDDD',\n          '#ABCABC', '#4169E1', '#C71585', '#FF4500', '#FEDCBA', '#46BFBD']\nDEFAULT_URL_RENDER_MODE = 'full'\nDEFAULT_PER_PAGE = 10\nLOG = logging.getLogger('bukuserver.views')\n\n\nclass CustomAdminIndexView(AdminIndexView):\n    @expose(\"/\")\n    def index(self):\n        return self.render(\"bukuserver/home.html\", form=forms.HomeForm())\n\n    @expose('/', methods=['POST'])\n    def search(self):\n        \"redirect to bookmark search\"\n        form = forms.HomeForm()\n        regex, markers = form.regex.data, form.markers.data\n        deep, all_keywords = (x and not regex for x in [form.deep.data, form.all_keywords.data])\n        flt = bs_filters.BookmarkBukuFilter(deep=deep, regex=regex, markers=markers, all_keywords=all_keywords)\n        vals = ([('', form.keyword.data)] if not markers else enumerate(buku.split_by_marker(form.keyword.data)))\n        url = url_for('bookmark.index_view', **{filter_key(flt, idx): val for idx, val in vals})\n        return redirect(url)\n\n\ndef last_page(self):\n    \"\"\"Generic '/last_page' endpoint handler; based on\n    https://github.com/flask-admin/flask-admin/blob/v1.6.0/flask_admin/model/base.py#L1956-L1969 \"\"\"\n    # Grab parameters from URL\n    view_args = self._get_list_extra_args()\n\n    # Map column index to column name\n    sort_column = self._get_column_by_idx(view_args.sort)\n    if sort_column is not None:\n        sort_column = sort_column[0]\n\n    # Get page size\n    page_size = view_args.page_size or self.page_size\n\n    # Get count and data\n    count, data = self.get_list(-1, sort_column, view_args.sort_desc,\n                                view_args.search, view_args.filters, page_size=page_size)\n\n    args = request.args.copy()\n    args.setlist('page', [max(0, (count - 1) // page_size)])\n    return redirect(url_for('.index_view', **args))\n\n\ndef app_param(key, default=None):\n    return app.config.get(f'BUKUSERVER_{key}', default)\n\ndef readonly_check(self):\n    if app_param('READONLY'):\n        self.can_create = False\n        self.can_edit = False\n        self.can_delete = False\n\nclass ApplyFiltersMixin:  # pylint: disable=too-few-public-methods\n    def _apply_filters(self, models, filters):\n        for idx, name, value in filters:\n            if self._filters:\n                flt = self._filters[idx]\n                models = list(flt.apply(models, flt.clean(value)))\n        return models\n\n\nclass BookmarkModelView(BaseModelView, ApplyFiltersMixin):\n    @staticmethod\n    def _filter_arg(flt):\n        \"\"\"Exposes filter slugify logic; works because BookmarkModelView.named_filter_urls = True\"\"\"\n        return BaseModelView.get_filter_arg(BookmarkModelView, None, flt)\n\n    def _saved(self, id, url, ok=True):\n        if id and ok:\n            session['saved'] = id\n        else:\n            raise ValueError(_('Duplicate URL') if self.model.bukudb.get_rec_id(url) not in [id, None] else\n                             _('Rejected by the database'))\n\n    def _create_ajax_loader(self, name, options):\n        pass\n\n    def _list_entry(self, context: Any, model: Namespace, name: str) -> Markup:\n        LOG.debug(\"context: %s, name: %s\", context, name)\n        parsed_url = urlparse(model.url)\n        netloc = buku.get_netloc(model.url) or ''\n        get_index_view_url = functools.partial(url_for, \"bookmark.index_view\")\n        res = []\n        if netloc and not app_param('DISABLE_FAVICON'):\n            res += [f'<img class=\"favicon\" src=\"http://www.google.com/s2/favicons?domain={netloc}\"/> ']\n        title = model.title or _('<EMPTY TITLE>')\n        new_tab = app_param('OPEN_IN_NEW_TAB')\n        url_for_index_view_netloc = None\n        if netloc:\n            url_for_index_view_netloc = get_index_view_url(flt0_url_netloc_match=netloc)\n        _title = (escape(title) if self.url_render_mode == 'full' and (not netloc or not parsed_url.scheme) else\n                  link(title, model.url, new_tab=new_tab))\n        res += [f'<span class=\"title\" title=\"{model.url}\">{_title}</span>']\n        if self.url_render_mode == 'netloc' and url_for_index_view_netloc:\n            res += [f'<span class=\"netloc\"> ({link(netloc, url_for_index_view_netloc)})</span>']\n        if not parsed_url.scheme:\n            res += [f'<span class=\"link\">{escape(model.url)}</span>']\n        elif self.url_render_mode == 'full':\n            res += [f'<span class=\"link\">{link(model.url, model.url, new_tab=new_tab)}</span>']\n        tag_links = []\n        if netloc and self.url_render_mode != 'netloc' and url_for_index_view_netloc:\n            tag_links += [link(f'netloc:{netloc}', url_for_index_view_netloc, badge='success')]\n        for tag in filter(None, model.tags.split(',')):\n            tag_links += [link(tag, get_index_view_url(flt0_tags_contain=tag.strip()), badge='secondary')]\n        res += [f'<div class=\"tag-list\">{\"\".join(tag_links)}</div>']\n        description = model.description and f'<div class=\"description\">{escape(model.description)}</div>'\n        if description:\n            res += [description]\n        return Markup(\"\".join(res))\n\n    @pass_context\n    def get_detail_value(self, context, model, name):\n        value = super().get_detail_value(context, model, name)\n        if name == 'tags':\n            tags = (link(s.strip(), url_for('bookmark.index_view', flt0_tags_contain=s.strip()), badge='secondary')\n                    for s in (value or '').split(',') if s.strip())\n            return Markup(f'<div class=\"tag-list\">{\"\".join(tags)}</div>')\n        if name == 'url':\n            res, netloc, scheme = [], buku.get_netloc(value), urlparse(value).scheme\n            if netloc and not app_param('DISABLE_FAVICON', False):\n                icon = f'<img class=\"favicon\" title=\"netloc:{netloc}\" src=\"http://www.google.com/s2/favicons?domain={netloc}\"/>'\n                res += [link(icon, url_for('bookmark.index_view', flt0_url_netloc_match=netloc), html=True)]\n            elif netloc:\n                badge = f'<span class=\"netloc\">netloc:{escape(netloc)}</span>'\n                res += [link(badge, url_for('bookmark.index_view', flt0_url_netloc_match=netloc), html=True, badge='success')]\n            res += [escape(value) if not scheme else link(value, value, new_tab=app_param('OPEN_IN_NEW_TAB'))]\n            return Markup(f'<div class=\"link\">{\" \".join(res)}</div>')\n        return Markup(f'<div class=\"{name}\">{escape(value)}</div>')\n\n    can_set_page_size = True\n    can_view_details = True\n    column_filters = ['buku', 'id', 'url', 'title', 'tags', 'order']\n    column_list = ['entry']\n    column_labels = {'entry': _l('Entry'), 'id': _l('Index'), 'url': _l('URL'),\n                     'title': _l('Title'), 'tags': _l('Tags'), 'description': _l('Description')}\n    column_formatters = {'entry': _list_entry}\n    list_template = 'bukuserver/bookmarks_list.html'\n    create_modal = True\n    create_modal_template = \"bukuserver/bookmark_create_modal.html\"\n    create_template = \"bukuserver/bookmark_create.html\"\n    details_modal = True\n    details_modal_template = 'bukuserver/bookmark_details_modal.html'\n    details_template = 'bukuserver/bookmark_details.html'\n    edit_modal = True\n    edit_modal_template = \"bukuserver/bookmark_edit_modal.html\"\n    edit_template = \"bukuserver/bookmark_edit.html\"\n    named_filter_urls = True\n    extra_css = ['/static/bukuserver/css/' + it for it in ('bookmark.css', 'modal.css', 'list.css')]\n    extra_js = ['/static/bukuserver/js/' + it for it in ('last_page.js', 'filters_fix.js')]\n    last_page = expose('/last-page')(last_page)\n\n    def __init__(self, bukudb: buku.BukuDb, *args, **kwargs):\n        readonly_check(self)\n        self.bukudb = bukudb\n        custom_model = types.SimpleNamespace(bukudb=bukudb, __name__='bookmark')\n        super().__init__(custom_model, *args, **kwargs)\n\n    @property\n    def url_render_mode(self):\n        return app_param('URL_RENDER_MODE', DEFAULT_URL_RENDER_MODE)\n\n    @property\n    def page_size(self):\n        return app_param('PER_PAGE', DEFAULT_PER_PAGE)\n\n    @property\n    def page_size_options(self):\n        return tuple(sorted(set([self.page_size] + list(super().page_size_options))))\n\n    def get_safe_page_size(self, page_size):  # un-enforcing the restriction\n        return (page_size if self.can_set_page_size and page_size > 0 else self.page_size)\n\n    def create_form(self, obj=None):\n        form = super().create_form(obj)\n        if not form.data.get('csrf_token'):  # don't override POST data with URL arguments\n            form.url.data = request.args.get('link', form.url.data)\n            form.title.data = request.args.get('title', form.title.data)\n            form.description.data = request.args.get('description', form.description.data)\n            form.tags.data = request.args.get('tags', form.tags.data)\n            form.fetch.data = request.args.get('fetch', request.form.get('fetch', app_param('AUTOFETCH', True)))\n        return form\n\n    def create_model(self, form):\n        try:\n            model = types.SimpleNamespace(id=None, url=None, title=None, tags=None, description=None, fetch=None)\n            form.populate_obj(model)\n            vars(model).pop(\"id\")\n            self._on_model_change(form, model, True)\n            if not model.url:\n                raise ValueError(_('url invalid: %(url)s', url=model.url))\n            kwargs = {'url': model.url, 'fetch': model.fetch}\n            if model.tags.strip():\n                kwargs[\"tags_in\"] = buku.parse_tags([model.tags])\n            for key, item in ((\"title_in\", model.title), (\"desc\", model.description)):\n                if item.strip():\n                    kwargs[key] = item\n            vars(model)['id'] = self.model.bukudb.add_rec(**kwargs)\n            self._saved(model.id, model.url)\n        except Exception as ex:\n            if not self.handle_view_exception(ex):\n                msg = _('Failed to create record.')\n                flash('%(msg)s %(error)s' % {'msg': msg, 'error': _(str(ex))}, 'error')\n                LOG.exception(msg)\n            return False\n        self.after_model_change(form, model, True)\n        return model\n\n    def delete_model(self, model):\n        try:\n            self.on_model_delete(model)\n            res = self.bukudb.delete_rec(model.id, retain_order=True)\n        except Exception as ex:\n            if not self.handle_view_exception(ex):\n                msg = _('Failed to delete record.')\n                flash('%(msg)s %(error)s' % {'msg': msg, 'error': _(str(ex))}, 'error')\n                LOG.exception(msg)\n            return False\n        self.after_model_delete(model)\n        return res\n\n    def _from_filters(self, filters):\n        bukudb = self.bukudb\n        order = bs_filters.BookmarkOrderFilter.value(self._filters, filters)\n        buku_filters = [x for x in filters if x[1] == 'buku']\n        if buku_filters:\n            keywords = [x[2] for x in buku_filters]\n            mode_id = {x[0] for x in buku_filters}\n            if len(mode_id) > 1:\n                flash(_('Invalid search mode combination'), 'error')\n                return 0, []\n            try:\n                kwargs = self._filters[mode_id.pop()].params\n            except IndexError:\n                kwargs = {}\n            bookmarks = bukudb.searchdb(keywords, order=order, **kwargs)\n        else:\n            bookmarks = bukudb.get_rec_all(order=order)\n        return self._apply_filters(bookmarks or [], filters)\n\n    @expose('/reorder', methods=['POST'])\n    def refresh(self):\n        filters = json.loads(request.form['filters'])\n        order = bs_filters.BookmarkOrderFilter.value(self._filters, filters)\n        self.bukudb.reorder(order)\n        flash(_('Reordered bookmarks in DB'), 'success')\n        return redirect(url_for('.index_view'))\n\n    def get_list(self, page, sort_field, sort_desc, _, filters, page_size=None):\n        bookmarks = self._from_filters(filters)\n        count = len(bookmarks)\n        bookmarks = page_of(bookmarks, page_size, page)\n        data = []\n        for bookmark in bookmarks:\n            bm_sns = types.SimpleNamespace(id=None, url=None, title=None, tags=None, description=None)\n            for field in list(BookmarkField):\n                setattr(bm_sns, field.name.lower(), format_value(field, bookmark))\n            data.append(bm_sns)\n        return count, data\n\n    def get_one(self, id):\n        if id == 'random':\n            bookmarks = self._from_filters(self._get_list_filter_args())\n            bookmark = bookmarks and random.choice(bookmarks)\n        else:\n            bookmark = self.model.bukudb.get_rec_by_id(id)\n        if not bookmark:\n            return None\n        bm_sns = types.SimpleNamespace(id=None, url=None, title=None, tags=None, description=None)\n        for field in list(BookmarkField):\n            setattr(bm_sns, field.name.lower(), format_value(field, bookmark, spacing=' '))\n        session['netloc'] = buku.get_netloc(bookmark.url) or ''\n        return bm_sns\n\n    def get_pk_value(self, model):\n        return model.id\n\n    @expose('/swap', methods=['POST'])\n    def swap(self):\n        form = forms.SwapForm()\n        self.bukudb.swap_recs(form.id1.data, form.id2.data)\n        return redirect(request.form.get('url', url_for('bookmark.index_view')))\n\n    def scaffold_list_columns(self):\n        return [x.name.lower() for x in BookmarkField]\n\n    def scaffold_list_form(self, widget=None, validators=None):\n        pass\n\n    def scaffold_sortable_columns(self):\n        \"\"\"Returns a dictionary of sortable columns.\n\n        from flask-admin docs:\n        `If your backend does not support sorting, return None or an empty dictionary.`\n        \"\"\"\n        return {}\n\n    def scaffold_filters(self, name):\n        res = []\n        if name == 'buku':\n            values_combi = sorted(itertools.product([True, False], repeat=4))\n            for markers, all_keywords, deep, regex in values_combi:\n                kwargs = {'markers': markers, 'all_keywords': all_keywords, 'deep': deep, 'regex': regex}\n                if not (regex and (deep or all_keywords)):\n                    res += [bs_filters.BookmarkBukuFilter(**kwargs)]\n        elif name == 'order':\n            res += [bs_filters.BookmarkOrderFilter(field)\n                    for field in bs_filters.BookmarkOrderFilter.FIELDS]\n        elif name == BookmarkField.ID.name.lower():\n            res += [\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.EQUAL),\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.NOT_EQUAL),\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.IN_LIST),\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.NOT_IN_LIST),\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.GREATER),\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.SMALLER),\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.TOP_X),\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.BOTTOM_X),\n            ]\n        elif name == BookmarkField.URL.name.lower():\n\n            def netloc_match_func(query, value, index):\n                return filter(lambda x: (buku.get_netloc(x[index]) or '') == value, query)\n\n            res += [\n                bs_filters.BookmarkBaseFilter(name, _l('netloc match'), netloc_match_func),\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.EQUAL),\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.NOT_EQUAL),\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.IN_LIST),\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.NOT_IN_LIST),\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.CONTAINS),\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.NOT_CONTAINS),\n            ]\n        elif name == BookmarkField.TITLE.name.lower():\n            res += [\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.EQUAL),\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.NOT_EQUAL),\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.IN_LIST),\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.NOT_IN_LIST),\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.CONTAINS),\n                bs_filters.BookmarkBaseFilter(name, filter_type=FilterType.NOT_CONTAINS),\n            ]\n        elif name == BookmarkField.TAGS.name.lower():\n\n            def get_list_from_buku_tags(item):\n                return [x.strip() for x in item.split(\",\")]\n\n            def tags_contain_func(query, value, index):\n                for item in query:\n                    if value in get_list_from_buku_tags(item[index]):\n                        yield item\n\n            def tags_not_contain_func(query, value, index):\n                for item in query:\n                    if value not in get_list_from_buku_tags(item[index]):\n                        yield item\n\n            res += [\n                bs_filters.BookmarkBaseFilter(name, _l('contain'), tags_contain_func),\n                bs_filters.BookmarkBaseFilter(name, _l('not contain'), tags_not_contain_func),\n                bs_filters.BookmarkTagNumberEqualFilter(name, _l('number equal')),\n                bs_filters.BookmarkTagNumberNotEqualFilter(name, _l('number not equal')),\n                bs_filters.BookmarkTagNumberGreaterFilter(name, _l('number greater than')),\n                bs_filters.BookmarkTagNumberSmallerFilter(name, _l('number smaller than')),\n            ]\n        elif name in self.scaffold_list_columns():\n            pass\n        else:\n            return super().scaffold_filters(name)\n        return res\n\n    def scaffold_form(self):\n        return forms.BookmarkForm\n\n    def update_model(self, form: forms.BookmarkForm, model: Namespace):\n        res = False\n        try:\n            form.populate_obj(model)\n            self._on_model_change(form, model, False)\n            res = self.bukudb.update_rec(\n                model.id,\n                url=model.url,\n                title_in=model.title,\n                tags_in=buku.parse_tags([model.tags]),\n                desc=model.description,\n            )\n            self._saved(model.id, model.url, res)\n        except Exception as ex:\n            if not self.handle_view_exception(ex):\n                msg = _('Failed to update record.')\n                flash('%(msg)s %(error)s' % {'msg': msg, 'error': _(str(ex))}, 'error')\n                LOG.exception(msg)\n            return False\n        self.after_model_change(form, model, False)\n        return res\n\n\nclass TagModelView(BaseModelView, ApplyFiltersMixin):\n    def _create_ajax_loader(self, name, options):\n        pass\n\n    def _name_formatter(self, context, model, name):\n        data = getattr(model, name)\n        query, title = (({'flt0_tags_contain': data}, data) if data else\n                        ({'flt0_tags_number_equal': 0}, _('<UNTAGGED>')))\n        return Markup(link(title, url_for('bookmark.index_view', **query)))\n\n    can_create = False\n    can_set_page_size = True\n    column_filters = ['name', 'usage_count']\n    column_labels = {'name': _lp('tag', 'Name'), 'usage_count': _lp('tag', 'Usage Count')}\n    column_formatters = {'name': _name_formatter}\n    list_template = 'bukuserver/tags_list.html'\n    edit_template = \"bukuserver/tag_edit.html\"\n    named_filter_urls = True\n    extra_css = ['/static/bukuserver/css/list.css']\n    extra_js = ['/static/bukuserver/js/' + it for it in ('last_page.js', 'filters_fix.js')]\n    last_page = expose('/last-page')(last_page)\n\n    def _refresh(self):\n        app.logger.info('Refreshing tags cache')\n        self.refreshed, self.all_tags = arrow.now(), self.bukudb.get_tag_all()\n\n    def __init__(self, bukudb, *args, **kwargs):\n        readonly_check(self)\n        self.bukudb = bukudb\n        self._refresh()\n        custom_model = types.SimpleNamespace(bukudb=bukudb, __name__='tag')\n        super().__init__(custom_model, *args, **kwargs)\n\n    @property\n    def page_size(self):\n        return app_param('PER_PAGE', DEFAULT_PER_PAGE)\n\n    @property\n    def page_size_options(self):\n        return tuple(sorted(set([self.page_size] + list(super().page_size_options))))\n\n    def get_safe_page_size(self, page_size):  # un-enforcing the restriction\n        return (page_size if self.can_set_page_size and page_size > 0 else self.page_size)\n\n    @expose('/refresh', methods=['POST'])\n    def refresh(self):\n        self._refresh()\n        return redirect(request.referrer or url_for('.index_view'))\n\n    def scaffold_list_columns(self):\n        return [\"name\", \"usage_count\"]\n\n    def scaffold_sortable_columns(self):\n        return {x: x for x in self.scaffold_list_columns()}\n\n    def scaffold_form(self):\n        class CustomForm(FlaskForm):  # pylint: disable=too-few-public-methods\n            name = wtforms.StringField(_lp('tag', 'Name'), validators=[wtforms.validators.DataRequired()])\n\n        return CustomForm\n\n    def scaffold_list_form(self, widget=None, validators=None):\n        pass\n\n    def get_list(\n        self,\n        page: int,\n        sort_field: str,\n        sort_desc: bool,\n        search: Optional[Any],\n        filters: List[Tuple[int, str, str]],\n        page_size: int = None,\n    ) -> Tuple[int, List[types.SimpleNamespace]]:\n        app.logger.debug('search: %s', search)\n        if (arrow.now() - self.refreshed).seconds > 60:  # automatic refresh if more than a minute passed since last one\n            self._refresh()\n        else:\n            app.logger.debug('Tags cache refreshed %ss ago', (arrow.now() - self.refreshed).seconds)\n        tags = self._apply_filters(sorted(self.all_tags[1].items()), filters)\n        sort_field_dict = {\"usage_count\": 1, \"name\": 0}\n        if sort_field in sort_field_dict:\n            tags = sorted(tags, reverse=sort_desc, key=lambda x: x[sort_field_dict[sort_field]])\n        count = len(tags)\n        tags = page_of(tags, page_size, page)\n        data = []\n        for name, usage_count in tags:\n            tag_sns = types.SimpleNamespace(name=None, usage_count=None)\n            tag_sns.name, tag_sns.usage_count = name, usage_count\n            data.append(tag_sns)\n        return count, data\n\n    def get_pk_value(self, model):\n        return model.name\n\n    def get_one(self, id):\n        tags = self.all_tags[1]\n        tag_sns = types.SimpleNamespace(name=id, usage_count=tags.get(id, 0))\n        return tag_sns\n\n    def scaffold_filters(self, name):\n        res = []\n\n        def top_most_common_func(query, value, index):\n            counter = Counter(x[index] for x in query)\n            most_common = counter.most_common(value)\n            most_common_item = {x for x, count in most_common}\n            return filter((lambda x: x[index] in most_common_item), query)\n\n        res += [\n            bs_filters.TagBaseFilter(name, filter_type=FilterType.EQUAL),\n            bs_filters.TagBaseFilter(name, filter_type=FilterType.NOT_EQUAL),\n            bs_filters.TagBaseFilter(name, filter_type=FilterType.IN_LIST),\n            bs_filters.TagBaseFilter(name, filter_type=FilterType.NOT_IN_LIST),\n        ]\n        if name == \"usage_count\":\n            res += [\n                bs_filters.TagBaseFilter(name, filter_type=FilterType.GREATER),\n                bs_filters.TagBaseFilter(name, filter_type=FilterType.SMALLER),\n                bs_filters.TagBaseFilter(name, filter_type=FilterType.TOP_X),\n                bs_filters.TagBaseFilter(name, filter_type=FilterType.BOTTOM_X),\n                bs_filters.TagBaseFilter(name, _l('top most common'), top_most_common_func),\n            ]\n        elif name == \"name\":\n            res += [\n                bs_filters.TagBaseFilter(name, filter_type=FilterType.CONTAINS),\n                bs_filters.TagBaseFilter(name, filter_type=FilterType.NOT_CONTAINS),\n            ]\n        else:\n            return super().scaffold_filters(name)\n        return res\n\n    def delete_model(self, model):\n        res = None\n        try:\n            self.on_model_delete(model)\n            res = self.bukudb.delete_tag_at_index(0, model.name, chatty=False)\n            self._refresh()\n        except Exception as ex:\n            if not self.handle_view_exception(ex):\n                msg = _('Failed to delete record.')\n                flash('%(msg)s %(error)s' % {'msg': msg, 'error': _(str(ex))}, 'error')\n                LOG.exception(msg)\n            return False\n        self.after_model_delete(model)\n        return res\n\n    def update_model(self, form, model):\n        try:\n            original_name = model.name\n            form.populate_obj(model)\n            self._on_model_change(form, model, False)\n            names = {s for s in re.split(r'\\s*,\\s*', model.name.lower().strip()) if s}\n            assert names, 'Tag name cannot be blank.'  # deleting a tag should be done via a Delete button\n            self.bukudb.replace_tag(original_name, names)\n            self._refresh()\n        except Exception as ex:\n            if not self.handle_view_exception(ex):\n                msg = _('Failed to update record.')\n                flash('%(msg)s %(error)s' % {'msg': msg, 'error': _(str(ex))}, 'error')\n                LOG.exception(msg)\n            return False\n        self.after_model_change(form, model, False)\n        return True\n\n    def create_model(self, form):\n        pass\n\n\nclass StatisticView(BaseView):  # pylint: disable=too-few-public-methods\n    _data = None\n    extra_css = ['/static/bukuserver/css/modal.css']\n\n    def __init__(self, bukudb, *args, **kwargs):\n        self.bukudb = bukudb\n        super().__init__(*args, **kwargs)\n\n    @expose(\"/\", methods=(\"GET\", \"POST\"))\n    def index(self):\n        data = StatisticView._data\n        if not data or request.method == 'POST':\n            all_bookmarks = self.bukudb.get_rec_all()\n            netlocs = [buku.get_netloc(x.url) or '' for x in all_bookmarks]\n            tags = [s for x in all_bookmarks for s in x.taglist]\n            titles = [x.title for x in all_bookmarks]\n            data = StatisticView._data = {\n                'netlocs': sorted_counter(netlocs),\n                'tags': sorted_counter(tags),\n                'titles': sorted_counter(titles, min_count=1),\n                'generated': arrow.now(),\n            }\n\n        datetime = data['generated']\n        return self.render(\n            'bukuserver/statistic.html',\n            netlocs=CountedData(data['netlocs']),\n            tags=CountedData(data['tags']),\n            titles=CountedData(data['titles']),\n            datetime=datetime,\n            datetime_text=datetime.humanize(arrow.now(), granularity='second'),\n        )\n\n\ndef page_of(items, size, idx):\n    try:\n        return chunks(items, size)[idx] if size and items else items\n    except IndexError:\n        return []\n\ndef filter_key(flt, idx=''):\n    if isinstance(idx, int) and idx > 9:\n        idx = (chr(ord('A') + idx-10) if idx < 36 else chr(ord('a') + idx-36))\n    return 'flt' + str(idx) + '_' + BookmarkModelView._filter_arg(flt)\n\ndef format_value(field, bookmark, spacing=''):\n    s = bookmark[field.value]\n    return s if field != BookmarkField.TAGS else (s or '').strip(',').replace(',', ','+spacing)\n\ndef link(text, url, new_tab=False, html=False, badge=''):\n    target = ('' if not new_tab else ' target=\"_blank\"')\n    cls = ('' if not badge else f' class=\"btn badge badge-{badge}\"')\n    return f'<a{cls} href=\"{escape(url)}\"{target}>{text if html else escape(text)}</a>'\n\n\nColoredData = namedtuple('ColoredData', 'name amount color')\n\nclass CountedData(list):\n    def __init__(self, counter):\n        self._counter = Counter(counter)\n        data = self._counter.most_common(len(COLORS))\n        self += [ColoredData(name, amount, color) for (name, amount), color in zip(data, COLORS)]\n\n    @property\n    def cropped(self):\n        return len(self) < len(self._counter)\n\n    @property\n    def all(self):\n        return self._counter.most_common()\n"
  },
  {
    "path": "bukuserver-runner/README.md",
    "content": "# Bukuserver runner\n\nThis tool can be used to run and restart Bukuserver, switching databases between runs. It has no third-party dependencies, allowing to run Bukuserver sandboxed in a virtualenv as easily as the system-wide install (which is especifally useful for development).\n\nI suggest installing/symlinking it system-wide (e.g. as an `/usr/local/bin/buku-server` executable). Either of the `*.desktop` files can be edited according to match your setup and installed in your `local/share/applications/` folder for access from system menu.\n\nOn Windows, you can create a shortcut file pointing to any Python executable (`python.exe` for windowed mode, `pythonw.exe` for headless) with added CLI arguments: path to `buku-server.py` followed by `--stop-if-running`.\n\nNote that windowed mode may be necessary if you want to see Bukuserver logs, or use noGUI mode (see below). The terminal window can be minimized to tray when not in use, by a program like [KDocker](https://github.com/user-none/KDocker), [RBTray](https://github.com/benbuck/rbtray) or [SmartSystemMenu](https://github.com/AlexanderPro/SmartSystemMenu).\n\n## Usage\n\nWhen running `buku-server.py` without arguments, it will prompt for database file, then start the Bukuserver. These actions will be repeated once Bukuserver stops running (e.g. after hitting `Ctrl+C`). The script will quit if you cancel the prompt.\n\nIn GUI mode, the prompt is implemented as 2 dialogs; a list of databases to choose from, and a text input for creating a new DB. In the shell mode, you can type in DB number from the list, or a new DB name. Note that DB names must be valid filenames in your system (sans the `.db` extension). These files are located in your Buku settings folder (along with the default `bookmarks.db` file).\n\nRunning `buku-server.py --stop` will kill the currently running Bukuserver process (thus allowing to restart it in the background, like a daemon). `buku-server.py --stop-if-running` will either start the script or kill Bukuserver if it's running already.\n\n<details><summary><h3>Screenshots</h3></summary>\n\n![DB selection dialog](https://github.com/Buku-dev/docs/blob/v4.9-bootstrap3/bukuserver/runner-script/db-selection.png \"DB selection dialog\")  \n_DB selection dialog – shown on startup (unless no DB files were found); initially the previous DB is selected_\n\n![DB creation dialog](https://github.com/Buku-dev/docs/blob/v4.9-bootstrap3/bukuserver/runner-script/db-creation.png \"DB creation dialog\")  \n_DB creation dialog – shown if no DB was selected (or none found)_\n\n![DB exists](https://github.com/Buku-dev/docs/blob/v4.9-bootstrap3/bukuserver/runner-script/existing-db-confirmation.png \"DB exists\")  \n_A confirmation dialog is shown if new DB name is taken already_\n\n![DB naming error](https://github.com/Buku-dev/docs/blob/v4.9-bootstrap3/bukuserver/runner-script/invalid-db-error.png \"DB naming error\")  \n_DB name must be a valid filename, sans the `.db` extension (invalid chars: `/` on Linux, or any of `<>:\"/\\|?*` on Windows)_\n\n![no-GUI mode](https://github.com/Buku-dev/docs/blob/v4.9-bootstrap3/bukuserver/runner-script/non-gui.png \"no-GUI mode\")  \n_DB selection prompt in console shell/no-GUI mode (`BUKU_NOGUI=y`)_\n</details>\n\n## Environment variables\n\nThe script behaviour can be configured by setting the following environment variables:\n* `BUKUSERVER` specifies path to your Bukuserver executable or Buku source directory.\n* `BUKU_DEVMODE` – if not empty, Bukuserver will be run in development mode. Normally used with source directory in `BUKUSERVER`.\n* `BUKU_VENV` overrides path to your virtualenv sandbox (default depends on whether `BUKU_DEVMODE` is set):\n  - when devmode is off, the virtualenv location defaults to a `venv/` folder in your Buku settings directory;\n  - when devmode is on, the virtualenv location defaults to a `venv/` folder in the source directory.\n* `BUKU_NOGUI` – if not empty, fallback shell prompt will be used (also happens if Tkinter is not present in your Python installation).\n* `BUKU_DEFAULT_DBDIR` – specify directory for DB selection (same as Buku itself).\n\nDefault values for all of these (as well as for `BUKUSERVER_` options) can be specified in a `bukuserver.env` file in your Buku settings folder:\n```sh\n# ~/.local/share/buku/bukuserver.env\nBUKUSERVER='~/Sources/buku/'  # when running from sources\nBUKUSERVER_THEME=slate\nBUKUSERVER_DISABLE_FAVICON=false\nBUKUSERVER_OPEN_IN_NEW_TAB=true\n```\n"
  },
  {
    "path": "bukuserver-runner/buku-server-headless.desktop",
    "content": "# This file can be used as a windowless startup/restart shortcut on Linux desktop/menu (after editing the Exec= command line)\n[Desktop Entry]\nIcon=bookmark-add\nName=Bukuserver (headless)\nGenericName=Bookmaks manager\nComment=WebUI for the buku bookmarks manager\nCategories=Utility;Network;\nExec={BUKU_SERVER} --stop-if-running\n#Exec=/usr/local/bin/buku-server --stop-if-running\n"
  },
  {
    "path": "bukuserver-runner/buku-server.desktop",
    "content": "# This file can be used as a windowed startup/restart shortcut on Linux desktop/menu (after editing the Exec= command line)\n[Desktop Entry]\nIcon=bookmark-add\nName=Bukuserver\nGenericName=Bookmaks manager\nComment=WebUI for the buku bookmarks manager\nCategories=Utility;Network;\nExec={TERMINAL} -e '{BUKU_SERVER} --stop-if-running'\n#Exec=xfce4-terminal -e '/usr/local/bin/buku-server --stop-if-running'\n"
  },
  {
    "path": "bukuserver-runner/buku-server.py",
    "content": "#!/usr/bin/env python\n# Usage: `buku-server.py` starts up the server, `buku-server.py --stop` sends TERM to the already running server\n#        `buku-server.py --stop-if-running` will either start the script or kill Bukuserver if it's running already\nfrom signal import signal, SIGINT\nfrom contextlib import contextmanager\nfrom os import environ as env\nimport sys\nimport os\nimport re\nimport shlex\nimport csv\nimport venv\nimport subprocess\n\nTITLE = re.sub(r'\\.py$', '', os.path.basename(__file__))\nIS_WINDOWS = sys.platform == 'win32'\n\nis_path = lambda s: ('/' in s or os.sep in s or s in ('.', '..'))\nin_venv = lambda virtualenv, name: os.path.join(virtualenv, ('Scripts' if IS_WINDOWS else 'bin'), name)\nset_title = lambda s: (print(f'\\033]2;{s}\\007', end='') if not IS_WINDOWS else run(f'title {s}', shell=True))\nunexpand_user = lambda s: re.sub(r'^' + re.escape(os.path.expanduser('~')), '~', s)\n\ntry:\n    from tkinter.messagebox import showerror, askyesno\n    from tkinter.simpledialog import askstring, Dialog\n    from tkinter import ttk\n    import tkinter as tk\n\n    class QueryList(Dialog):\n        def __init__(self, title, prompt, values, initial=None, parent=None):\n            self._prompt, self._vals, self._initial = prompt, values, (initial if initial in values else values[0])\n            super().__init__(parent, title)\n\n        def body(self, master):\n            w = ttk.Label(master, text=self._prompt, justify=tk.LEFT)\n            w.grid(row=0, padx=5, sticky=tk.W)\n            self._list = ttk.Treeview(master, show='tree')\n            self._list.grid(row=1, padx=5, sticky=tk.W+tk.E)\n            scroll = ttk.Scrollbar(master)\n            scroll.grid(row=1, padx=5, sticky=tk.E+tk.N+tk.S)\n            self._list.config(yscrollcommand=scroll.set)\n            scroll.config(command=self._list.yview)\n            self._keys = [self._list.insert('', 'end', text=s) for s in self._vals]\n            self.select(self._vals.index(self._initial))\n            self._list.bind('<KeyPress>', self.onkeypress)\n            self._list.bind('<Double-1>', lambda _: self.after('idle', self.ok))\n            return self._list\n\n        def select(self, index):\n            self._list.see(self._keys[index])\n            self._list.focus(self._keys[index])\n            self._list.selection_set(self._keys[index])\n\n        def onkeypress(self, evt):\n            match = [i for i, s in enumerate(self._vals) if s.startswith(evt.char)]\n            if evt.char and match:\n                cur = self._list.index(self._list.focus())\n                self.select(cur+1 if self._vals[cur].startswith(evt.char) and cur+1 in match else match[0])\n\n        def validate(self):\n            self.result = self._list.item(self._list.focus())['text']\n            return True\n\n    asklist = lambda title, prompt, values, initial=None: QueryList(title, prompt, values, initial=initial).result\n    GUI = True\nexcept ImportError:\n    GUI = False\n    print('Failed to initialize GUI', file=sys.stderr)\n\ndef is_valid_filepath(path):\n    try:\n        os.lstat(path)\n    except FileNotFoundError:\n        pass\n    except Exception:\n        return False\n    if os.path.isdir(path) or path.endswith(os.path.sep):\n        return False  # directory\n    drive, s = os.path.splitdrive(path)\n    return not IS_WINDOWS or not re.search(r'[<>:\"|?*]', s)\n\n@contextmanager\ndef ignore_interrupt():  # temporarily disables raising KeyboardInterrupt on Ctrl+C\n    old_handler = signal(SIGINT, lambda sig, frame: None)\n    yield\n    signal(SIGINT, old_handler)\n\ndef run(command, *, shell=IS_WINDOWS, check=True):\n    try:\n        command = (os.path.expandvars(command) if isinstance(command, str) else [os.path.expandvars(s) for s in command])\n        with ignore_interrupt():\n            return subprocess.run(command, shell=shell, check=check)\n    except Exception as e:\n        print(e, file=sys.stderr)\n        sys.exit(1)\n\ndef parse_csv(text):\n    text = (text.decode() if isinstance(text, bytes) else str(text)).strip()\n    keys, lines = None, re.split(r'[\\r\\n]+', text)\n    for row in csv.reader(lines):\n        if not keys:\n            keys = row\n        else:\n            yield dict(zip(keys, row))\n\ndef find_process(query, regex):\n    if IS_WINDOWS:\n        query = str(query or \"name LIKE '%'\")\n        command = ['wmic', 'process', 'where', query, 'get', 'commandline,processid', '/format:csv']\n        output = subprocess.check_output(command, shell=True)\n        for process in parse_csv(output):\n            if re.search(regex, process['CommandLine']):\n                return int(process['ProcessId'])\n    else:\n        command = 'ps x -o pid,cmd | awk ' + shlex.quote(f'$2 ~ /{query or \".*\"}/')\n        output = subprocess.check_output(command, shell=True)\n        for line in output.decode().splitlines():\n            pid, cmdline = line.lstrip().split(' ', 1)\n            if re.search(regex, cmdline):\n                return pid\n    return None  # nothing found\n\ndef find_bukuserver_process():\n    if not IS_WINDOWS:\n        return find_process(r'(^|\\/)python[.0-9]*$', r'\\b(bukuserver(/server\\.py)?) run$')\n    return find_process('name like \"python%.exe\"', r'\\b(bukuserver-script\\.py|bukuserver([/\\\\]server\\.py)?)\"? run$')\n\ndef kill_process(pid):\n    run(['kill', str(pid)] if not IS_WINDOWS else ['taskkill', '/F', '/pid', str(pid)])\n\ndef get_buku_config_dir():\n    path = (env.get('APPDATA') if IS_WINDOWS else\n            env.get('XDG_DATA_HOME') or os.path.join(os.path.expanduser('~'), '.local', 'share'))\n    return os.path.abspath(os.path.join(path, 'buku'))\n\ndef read_env_file(path):\n    regex = re.compile('([_A-Z]+)=(?:\"(.*)\"|\\'(.*)\\'|(.*))')\n    try:\n        with open(path, encoding='utf-8') as fin:\n            for line in fin:\n                tokens = shlex.split(line, comments=True)\n                if tokens and (m := regex.fullmatch(tokens[0])):\n                    yield (m[1], m[2] or m[3] or m[4])\n    except FileNotFoundError:\n        pass\n\n\ndef selectdb(dbdir, old=None, gui=GUI, title=TITLE):\n    default = dbdir == get_buku_config_dir()\n    old = old and old.removeprefix(dbdir+os.path.sep).removesuffix('.db')\n    if old and any(c in old for c in ['/', os.path.sep]):\n        old = None\n    if os.path.isdir(dbdir):\n        dbs = sorted(s[:-3] for s in os.listdir(dbdir) if s.lower().endswith('.db'))\n    else:\n        dbs = []\n    if gui:\n        _title = title + ('' if default else f' [{unexpand_user(dbdir)}]')\n        db = (None if not dbs else\n              asklist(_title, f'{\"Choose DB (or click Cancel to create new DB)\":80}', dbs, initial=old or 'bookmarks'))\n        while not db:\n            db = askstring(_title, f'{\"Create new DB?\":90}', initialvalue=old or 'bookmarks')\n            if db is None:\n                print('No name given, qutting', file=sys.stderr)\n                return None\n            dbfile = os.path.join(dbdir, db+'.db')\n            if not db or any(c in db for c in ['/', os.path.sep]) or not is_valid_filepath(dbfile):\n                showerror(_title, f'Invalid DB name: \"{db}\"')\n                db = None\n            elif os.path.exists(dbfile):\n                if not askyesno(_title, f'\"{db}\" exists already. Open anyway?'):\n                    db = None\n    else:\n        db, _title = None, ('' if default else f' [{unexpand_user(dbdir)}]')\n        while not db:\n            try:\n                print(f'\\nType DB name or index (0 to quit){_title}:')\n                for idx, name in enumerate(dbs, start=1):\n                    print(f'{idx}. {name}')\n                try:\n                    db = input('> ' if not old else f'> [{old}] ').strip() or old or 'bookmarks'\n                except EOFError as e:\n                    raise KeyboardInterrupt from e\n            except KeyboardInterrupt:\n                with ignore_interrupt():\n                    print()\n                    print('Input cancelled', file=sys.stderr)\n                    return None\n            try:\n                idx = int(db)\n                if idx == 0:\n                    print('Entered \"0\", quitting', file=sys.stderr)\n                    return None\n                if idx > 0:\n                    db = dbs[idx-1]\n                    break\n            except IndexError:\n                print('No such index!', file=sys.stderr)\n                db = None\n                continue\n            except ValueError:\n                pass  # not an index\n            dbfile = os.path.join(dbdir, db+'.db')\n            if not db or any(c in db for c in ['/', os.path.sep]) or not is_valid_filepath(dbfile):\n                print(f'Invalid DB name: \"{db}\"', file=sys.stderr)\n                db = None\n            elif not os.path.exists(dbfile):\n                if input(f'\"{db}\" does not exist yet. Create? [Y/n] ').upper().strip() == 'N':\n                    db = None\n    return db and os.path.join(dbdir, db+'.db')\n\ndef load_virtualenv(virtualenv, devmode=False, reinstall=False):\n    print(f'Using {os.path.abspath(virtualenv)}')\n    venv.create(virtualenv, with_pip=True, prompt='buku')\n    run([in_venv(virtualenv, 'python'), '-m', 'pip', 'install', '--upgrade', 'pip'])\n    if reinstall:\n        env.get('BUKUSERVER_LOCALE') and run([in_venv(virtualenv, 'pip'), 'install', 'flask-babel'])\n        if not devmode:\n            run([in_venv(virtualenv, 'pip'), 'install', '.[server]'])\n        else:\n            run([in_venv(virtualenv, 'pip'), 'install', '--editable', '.[server]'])\n\ndef prepare_vars():\n    confdir = get_buku_config_dir()\n    for name, value in read_env_file(os.path.join(confdir, 'bukuserver.env')):\n        print(f'default:{name}={shlex.quote(value)}')\n        env.setdefault(name, value)\n    workdir, exec, devmode = None, env.get('BUKUSERVER') or '', bool(env.get('BUKU_DEVMODE'))\n    devmode and env.setdefault('BUKUSERVER_DEBUG', 'true')\n    if exec and os.path.isdir(os.path.expanduser(exec)):\n        workdir, exec = exec, os.path.join('bukuserver', 'server.py')\n    return {\n        'dbdir': os.path.abspath(os.path.expanduser(env.get('BUKU_DEFAULT_DBDIR') or confdir)),\n        'devmode': devmode,\n        'gui': GUI and not env.get('BUKU_NOGUI'),\n        'exec': exec or 'bukuserver',\n        'workdir': workdir,\n        'virtualenv': env.get('BUKU_VENV') or (workdir and os.path.join(('.' if devmode else confdir), 'venv')),\n    }\n\ndef run_repeatedly(dbdir, devmode=False, gui=GUI, exec=None, workdir=None, virtualenv=None):\n    virtualenv = virtualenv and os.path.expanduser(virtualenv)\n    if workdir:\n        os.chdir(os.path.expanduser(workdir))\n    elif virtualenv:\n        os.chdir(virtualenv)\n        virtualenv = '.'\n    set_title(TITLE + ('' if dbdir == get_buku_config_dir() else f' [{dbdir}]'))\n    virtualenv and load_virtualenv(virtualenv, devmode=devmode, reinstall=bool(workdir))\n    command = [os.path.expanduser(exec), 'run']\n    if exec.endswith('.py'):\n        command = ['python'] + command\n    elif exec == 'bukuserver':\n        command = ['python', '-m'] + command\n    if virtualenv:\n        command[0] = in_venv(virtualenv, command[0])\n    set_title(f'{TITLE} [{shlex.join(command)}]')\n    db = env.get('BUKUSERVER_DB_FILE') or os.path.join(dbdir, 'bookmarks.db')\n    while True:\n        print('Running Bukuserver…')\n        if not (db := selectdb(dbdir, gui=gui, old=db)):\n            break\n        env['BUKUSERVER_DB_FILE'] = db\n        print(f'BUKUSERVER_DB_FILE={db}')\n        run(command, check=False)\n\nif __name__ == '__main__':\n    if (pid := find_bukuserver_process()):\n        if any(s in sys.argv for s in ['--stop', '--stop-if-running']):\n            print(f'Killing process {pid}')\n            kill_process(pid)\n            sys.exit()\n        else:\n            print('Already running bukuserver!', file=sys.stderr)\n            sys.exit(1)\n    if '--stop' in sys.argv:\n        print('Could not find a running bukuserver process!', file=sys.stderr)\n        sys.exit(1)\n    run_repeatedly(**prepare_vars())\n"
  },
  {
    "path": "docker-compose/docker-compose.yml",
    "content": "services:\n  bukuserver:\n    image: bukuserver/bukuserver\n    restart: unless-stopped\n    environment:\n      - BUKUSERVER_PER_PAGE=100\n      - BUKUSERVER_OPEN_IN_NEW_TAB=true\n      # - BUKUSERVER_SECRET_KEY=123456789012345678901234\n      # - BUKUSERVER_URL_RENDER_MODE=full\n      # - BUKUSERVER_DISABLE_FAVICON=false\n    ports:\n      - \"5001:5001\"\n    volumes:\n      - ./data:/root/.local/share/buku\n\n  nginx:\n    image: nginx:alpine\n    restart: unless-stopped\n    ports:\n      - \"80:80\"\n    volumes:\n      - ./data/nginx:/etc/nginx/conf.d\n      - ./data/basic_auth:/basic_auth\n\n"
  },
  {
    "path": "docs/source/buku.rst",
    "content": "buku module\n===========\n\n.. automodule:: buku\n    :members:\n    :undoc-members:\n    :show-inheritance:\n"
  },
  {
    "path": "docs/source/bukuserver.rst",
    "content": "bukuserver package\n==================\n\nbukuserver.filters module\n-------------------------\n\n.. automodule:: bukuserver.filters\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nbukuserver.forms module\n-----------------------\n\n.. automodule:: bukuserver.forms\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nbukuserver.response module\n--------------------------\n\n.. automodule:: bukuserver.response\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nbukuserver.server module\n------------------------\n\n.. automodule:: bukuserver.server\n   :members:\n   :undoc-members:\n   :show-inheritance:\n\nbukuserver.views module\n-----------------------\n\n.. automodule:: bukuserver.views\n   :members:\n   :undoc-members:\n   :show-inheritance:\n"
  },
  {
    "path": "docs/source/conf.py",
    "content": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n#\n# buku documentation build configuration file, created by\n# sphinx-quickstart on Thu Sep  7 12:54:59 2017.\n#\n# This file is execfile()d with the current directory set to its\n# containing dir.\n#\n# Note that not all possible configuration values are present in this\n# autogenerated file.\n#\n# All configuration values have a default; values that are commented out\n# serve to show the default.\n\n# If extensions (or modules to document with autodoc) are in another directory,\n# add these directories to sys.path here. If the directory is relative to the\n# documentation root, use os.path.abspath to make it absolute, like shown here.\n#\nimport os\nimport sys\n\nsys.path.insert(0, os.path.abspath('../../'))\n\n\n# -- General configuration ------------------------------------------------\n\n# If your documentation needs a minimal Sphinx version, state it here.\n#\n# needs_sphinx = '1.0'\n\n# Add any Sphinx extension module names here, as strings. They can be\n# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom\n# ones.\nextensions = [\n    \"myst_parser\",\n    \"sphinx.ext.autodoc\",\n    \"sphinx.ext.autosummary\",\n    \"sphinx.ext.githubpages\",\n    \"sphinx.ext.napoleon\",\n    \"sphinx.ext.viewcode\",\n]\n\n# Add any paths that contain templates here, relative to this directory.\ntemplates_path = ['_templates']\n\n# The suffix(es) of source filenames.\n# You can specify multiple suffix as a list of string:\n#\nsource_suffix = ['.rst', '.md']\n\n# The master toctree document.\nmaster_doc = 'index'\n\n# General information about the project.\nproject = \"buku\"\ncopyright = \"2015-2026, Arun Prakash Jana\"\nauthor = \"Arun Prakash Jana\"\n\n# The version info for the project you're documenting, acts as replacement for\n# |version| and |release|, also used in various other places throughout the\n# built documents.\n#\n# The short X.Y version.\nversion = ''\n# The full version, including alpha/beta/rc tags.\nrelease = ''\n\n# The language for content autogenerated by Sphinx. Refer to documentation\n# for a list of supported languages.\n#\n# This is also used if you do content translation via gettext catalogs.\n# Usually you set \"language\" from the command line for these cases.\nlanguage = None\n\n# List of patterns, relative to source directory, that match files and\n# directories to ignore when looking for source files.\n# This patterns also effect to html_static_path and html_extra_path\nexclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']\n\n# The name of the Pygments (syntax highlighting) style to use.\npygments_style = 'sphinx'\n\n# If true, `todo` and `todoList` produce output, else they produce nothing.\ntodo_include_todos = False\n\n\n# -- Options for HTML output ----------------------------------------------\n\n# The theme to use for HTML and HTML Help pages.  See the documentation for\n# a list of builtin themes.\n#\n# html_theme = 'alabaster'\nhtml_theme = \"sphinx_rtd_theme\"\n\n# Theme options are theme-specific and customize the look and feel of a theme\n# further.  For a list of options available for each theme, see the\n# documentation.\n#\n# html_theme_options = {}\n\n# Add any paths that contain custom static files (such as style sheets) here,\n# relative to this directory. They are copied after the builtin static files,\n# so a file named \"default.css\" will overwrite the builtin \"default.css\".\n# html_static_path = ['_static']\nhtml_static_path = []\n\n# Custom sidebar templates, must be a dictionary that maps document names\n# to template names.\n#\n# This is required for the alabaster theme\n# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars\nhtml_sidebars = {\n    '**': [\n        'about.html',\n        'navigation.html',\n        'relations.html',  # needs 'show_related': True theme option to display\n        'searchbox.html',\n        'donate.html',\n    ]\n}\n\n\n# -- Options for HTMLHelp output ------------------------------------------\n\n# Output file base name for HTML help builder.\nhtmlhelp_basename = 'bukudoc'\n\n\n# -- Options for LaTeX output ---------------------------------------------\n\nlatex_elements = {\n    # The paper size ('letterpaper' or 'a4paper').\n    #\n    # 'papersize': 'letterpaper',\n\n    # The font size ('10pt', '11pt' or '12pt').\n    #\n    # 'pointsize': '10pt',\n\n    # Additional stuff for the LaTeX preamble.\n    #\n    # 'preamble': '',\n\n    # Latex figure (float) alignment\n    #\n    # 'figure_align': 'htbp',\n}\n\n# Grouping the document tree into LaTeX files. List of tuples\n# (source start file, target name, title,\n#  author, documentclass [howto, manual, or own class]).\nlatex_documents = [\n    (master_doc, 'buku.tex', 'buku documentation',\n     'Arun Prakash Jana', 'manual'),\n]\n\n\n# -- Options for manual page output ---------------------------------------\n\n# One entry per manual page. List of tuples\n# (source start file, name, description, authors, manual section).\nman_pages = [\n    (master_doc, 'buku', 'buku documentation',\n     [author], 1)\n]\n\n\n# -- Options for Texinfo output -------------------------------------------\n\n# Grouping the document tree into Texinfo files. List of tuples\n# (source start file, target name, title, author,\n#  dir menu entry, description, category)\ntexinfo_documents = [\n    (master_doc, 'buku', 'buku documentation',\n     author, 'buku', 'One line description of project.',\n     'Miscellaneous'),\n]\n"
  },
  {
    "path": "docs/source/index.rst",
    "content": ".. buku documentation master file, created by\n   sphinx-quickstart on Thu Sep  7 12:54:59 2017.\n   You can adapt this file completely to your liking, but it should at least\n   contain the root `toctree` directive.\n\nbuku\n====\n\nBookmark manager like a text-based mini-web.\n\n.. toctree::\n   :maxdepth: 2\n   :caption: User guide\n\n   README.md\n\n.. toctree::\n   :glob:\n   :maxdepth: 2\n   :caption: Wiki\n   \n   wiki/*\n\n\n.. toctree::\n   :maxdepth: 2\n   :caption: Buku Documentation\n\n   buku <buku>\n\n.. toctree::\n   :maxdepth: 2\n   :caption: Bukuserver Documentation\n\n   bukuserver <bukuserver>\n\n\nIndices and tables\n==================\n\n* :ref:`genindex`\n* :ref:`modindex`\n* :ref:`search`\n"
  },
  {
    "path": "docs/source/modules.rst",
    "content": "buku\n==========\n\n.. toctree::\n   :maxdepth: 4\n\n   buku\n\nbukuserver\n==========\n\n.. toctree::\n   :maxdepth: 4\n\n   bukuserver\n\n"
  },
  {
    "path": "docs/source/tutorial_for_developer.md",
    "content": "# Tutorial for Developer\n\n## get buku database\n\n```python\n>>> import buku\n>>> bdb = buku.BukuDb()\n```\n\n## simplest way to add url to buku\n\n```python\n>>> rec_id = bdb.add_rec('http://example.com')\n>>> rec_id\n40296\n```\n\n## get record id from url\n\n```python\n>>> url = 'https://example.com'\n>>> rec_id = bdb.add_rec(url)\n... if rec_id == -1:\n...     rec_id = bdb.get_rec_id(url)\n>>> rec_id\n40296\n```\n\n## get url data from record id\n\n```python\n>>> rec = bdb.get_rec_by_id(40296)\n>>> rec\n(40296, 'http://example.com', 'Example Domain', ',', '', 0)\n```\n\n## get tag list\n\n```python\nrec[3].split(buku.delim)\n```\n"
  },
  {
    "path": "mypy.ini",
    "content": "[mypy]\nignore_missing_imports = True\n"
  },
  {
    "path": "packagecore.yaml",
    "content": "name: buku-cli\nmaintainer: Arun Prakash Jana <engineerarun@gmail.com>\nlicense: GPLv3\nsummary: Bookmark manager like a text-based mini-web.\nhomepage: https://github.com/jarun/buku\ncommands:\n  install:\n    - make PREFIX=\"/usr\" install DESTDIR=\"${BP_DESTDIR}\"\npackages:\n#  archlinux:\n#    builddeps:\n#      - make\n#    deps:\n#      - python\n#      - python-beautifulsoup4\n#      - python-certifi\n#      - python-cryptography\n#      - python-urllib3\n#    container: \"archlinux/base\"\n# centos no beautifulsoup4\n  centos7.5:\n    builddeps:\n      - make\n    deps:\n      - python\n#     - python-beautifulsoup4\n#     - python-certifi\n      - python-cryptography\n      - python-urllib3\n    commands:\n      pre:\n        - yum install epel-release\n  centos7.6:\n    builddeps:\n      - make\n    deps:\n      - python\n#     - python-beautifulsoup4\n#     - python-certifi\n      - python-cryptography\n      - python-urllib3\n  centos7.7:\n    builddeps:\n      - make\n    deps:\n      - python\n#     - python-beautifulsoup4\n#     - python-certifi\n      - python-cryptography\n      - python-urllib3\n  centos8.0:\n    builddeps:\n      - make\n    deps:\n      - python3\n#     - python3-beautifulsoup4\n#     - python3-certifi\n      - python3-cryptography\n      - python3-urllib3\n    commands:\n      precompile:\n        - dnf install python3 python3-cryptography python3-urllib3\n  debian9:\n    builddeps:\n      - make\n    deps:\n      - python3\n      - python3-bs4\n      - python3-certifi\n      - python3-cryptography\n      - python3-urllib3\n  debian10:\n    builddeps:\n      - make\n    deps:\n      - python3\n      - python3-bs4\n      - python3-certifi\n      - python3-cryptography\n      - python3-urllib3\n  fedora31:\n    builddeps:\n      - make\n    deps:\n      - python3\n      - python3-beautifulsoup4\n      - python3-certifi\n      - python3-cryptography\n      - python3-urllib3\n  fedora32:\n    builddeps:\n      - make\n    deps:\n      - python3\n      - python3-beautifulsoup4\n      - python3-certifi\n      - python3-cryptography\n      - python3-urllib3\n  opensuse15.1:\n    builddeps:\n      - make\n    deps:\n      - python3\n      - python3-beautifulsoup4\n      - python3-certifi\n      - python3-cryptography\n      - python3-urllib3\n  opensuse15.2:\n    builddeps:\n      - make\n    deps:\n      - python3\n      - python3-beautifulsoup4\n      - python3-certifi\n      - python3-cryptography\n      - python3-urllib3\n  opensuse.tumbleweed:\n    builddeps:\n      - make\n    deps:\n      - python3\n      - python3-beautifulsoup4\n      - python3-certifi\n      - python3-cryptography\n      - python3-urllib3\n  ubuntu16.04:\n    builddeps:\n      - make\n    deps:\n      - python3\n      - python3-bs4\n      - python3-certifi\n      - python3-cryptography\n      - python3-urllib3\n  ubuntu18.04:\n    builddeps:\n      - make\n    deps:\n      - python3\n      - python3-bs4\n      - python3-certifi\n      - python3-cryptography\n      - python3-urllib3\n  ubuntu20.04:\n    builddeps:\n      - make\n    deps:\n      - python3\n      - python3-bs4\n      - python3-certifi\n      - python3-cryptography\n      - python3-urllib3\n"
  },
  {
    "path": "pyproject.toml",
    "content": "[project]\nname = \"buku\"\ndescription = \"Bookmark manager like a text-based mini-web.\"\nkeywords = [\"cli\", \"bookmarks\", \"tag\", \"utility\"]\nreadme = \"README.md\"\nlicense = \"GPL-3.0-or-later\"\nauthors = [{ name = \"Arun Prakash Jana\", email = \"engineerarun@gmail.com\" }]\nclassifiers = [\n    \"Development Status :: 5 - Production/Stable\",\n    \"Environment :: Console\",\n    \"Intended Audience :: Developers\",\n    \"Intended Audience :: End Users/Desktop\",\n    \"Natural Language :: English\",\n    \"Operating System :: OS Independent\",\n    \"Programming Language :: Python :: 3\",\n    \"Programming Language :: Python :: 3 :: Only\",\n    \"Programming Language :: Python :: 3.10\",\n    \"Programming Language :: Python :: 3.11\",\n    \"Programming Language :: Python :: 3.12\",\n    \"Programming Language :: Python :: 3.13\",\n    \"Programming Language :: Python :: 3.14\",\n    \"Topic :: Internet :: WWW/HTTP :: Indexing/Search\",\n    \"Topic :: Utilities\",\n]\nrequires-python = \">=3.10\"\ndependencies = [\n    \"beautifulsoup4>=4.4.1\",\n    \"certifi\",\n    \"cryptography>=1.2.3\",\n    \"html5lib>=1.0.1\",\n    \"urllib3>=1.23,<3\",\n    \"pyreadline3; sys_platform == 'win32'\",\n    \"colorama>=0.4.6; sys_platform == 'win32'\",\n]\ndynamic = [\"version\"]\n\n[project.urls]\nHomepage = \"https://github.com/jarun/buku\"\nDocumentation = \"https://buku.readthedocs.io/en/latest\"\nFunding = \"https://github.com/sponsors/jarun\"\nSource = \"https://github.com/jarun/buku\"\nTracker = \"https://github.com/jarun/buku/issues\"\n\n[project.optional-dependencies]\nserver = [\n    \"arrow>=1.2.2\",\n    \"Flask-Admin>=2.0.0\",\n    \"flask-paginate>=2022.1.8\",\n    \"Flask-WTF>=1.0.1\",\n    \"Flask>=2.2.2\",\n    \"Jinja2>=3\",\n    \"flasgger\",\n]\nlocales = [\"Flask-Admin[translation]\"]\n\n[dependency-groups]\ndev = [\n    { include-group = \"tests\" },\n    { include-group = \"docs\" },\n    { include-group = \"packaging\" },\n]\ntests = [\n    \"attrs>=17.4.0\",\n    \"beautifulsoup4>=4.6.0\",\n    \"Click>=7.0\",\n    \"flake8>=3.4.1\",\n    \"hypothesis>=6.0.0\",\n    \"mypy-extensions==0.4.1\",\n    \"py>=1.5.0\",\n    \"pylint>=1.7.2\",\n    \"pytest-cov\",\n    \"pytest-recording>=0.12.1\",\n    \"pytest-timeout\",\n    \"pytest>=6.2.1\",\n    \"PyYAML>=4.2b1\",\n    \"tomli; python_version < '3.11'\",\n    \"vcrpy>=1.13.0\",\n    \"lxml\",\n    \"buku[server,locales]\",\n]\ndocs = [\n    \"myst-parser>=0.17.0\",\n    \"sphinx-autobuild>=2021.3.14\",\n    \"sphinx-rtd-theme>=1.0.0\",\n]\npackaging = [\"twine>=1.11.0\"]\n\n[project.scripts]\nbuku = \"buku:main\"\nbukuserver = \"bukuserver.server:cli\"\n\n[tool.setuptools]\npy-modules = [\"buku\"]\npackages.find.include = [\"bukuserver\", \"bukuserver.*\"]\n\n[tool.setuptools.data-files]\n\"share/man/man1\" = [\"*.1\"]\n\n[tool.setuptools.dynamic]\nversion = { attr = \"buku.__version__\" }\n\n[build-system]\nrequires = [\"setuptools>=77.0.3\"]\nbuild-backend = \"setuptools.build_meta\"\n"
  },
  {
    "path": "requirements.txt",
    "content": "# use setup.py for latest required package\nbeautifulsoup4>=4.4.1\ncertifi\ncryptography>=1.2.3\nhtml5lib>=1.0.1\nsetuptools\nurllib3>=1.23,<3\npyreadline3; sys_platform == 'win32'\ncolorama>=0.4.6; sys_platform == 'win32'\n"
  },
  {
    "path": "tests/.pylintrc",
    "content": "[MESSAGES CONTROL]\ndisable=\n  assigning-non-slot,\n  broad-except,\n  c-extension-no-member,\n  consider-using-f-string,\n  consider-using-with,\n  dangerous-default-value,\n  expression-not-assigned,\n  fixme,\n  global-statement,\n  global-variable-not-assigned,\n  import-error,\n  import-outside-toplevel,\n  invalid-name,\n  len-as-condition,\n  logging-format-interpolation,\n  lost-exception,\n  missing-docstring,\n  pointless-statement,\n  protected-access,\n  redefined-argument-from-local,\n  redefined-builtin,\n  redefined-outer-name,\n  too-many-arguments,\n  too-many-branches,\n  too-many-instance-attributes,\n  too-many-lines,\n  too-many-locals,\n  too-many-nested-blocks,\n  too-many-positional-arguments,\n  too-many-public-methods,\n  too-many-return-statements,\n  too-many-statements,\n  undefined-loop-variable,\n  ungrouped-imports,\n  unidiomatic-typecheck,\n  unnecessary-lambda,\n  unnecessary-lambda-assignment,\n  unsubscriptable-object,\n  unsupported-assignment-operation,\n  unused-argument,\n  unused-variable,\n[FORMAT]\nmax-line-length=139\n"
  },
  {
    "path": "tests/__init__.py",
    "content": ""
  },
  {
    "path": "tests/cassettes/test_buku/test_fetch_data_with_url[http---example.com-exp_res1].yaml",
    "content": "interactions:\n- request:\n    body: null\n    headers:\n      Accept:\n      - '*/*'\n      Accept-Encoding:\n      - gzip,deflate\n      Cookie:\n      - ''\n      DNT:\n      - '1'\n      User-Agent:\n      - Mozilla/5.0 (X11; Linux x86_64; rv:101.0) Gecko/20100101 Firefox/101.0\n    method: GET\n    uri: http://example.com/\n  response:\n    body:\n      string: \"<!doctype html>\\n<html>\\n<head>\\n    <title>Example Domain</title>\\n\\n\n        \\   <meta charset=\\\"utf-8\\\" />\\n    <meta http-equiv=\\\"Content-type\\\" content=\\\"text/html;\n        charset=utf-8\\\" />\\n    <meta name=\\\"viewport\\\" content=\\\"width=device-width,\n        initial-scale=1\\\" />\\n    <style type=\\\"text/css\\\">\\n    body {\\n        background-color:\n        #f0f0f2;\\n        margin: 0;\\n        padding: 0;\\n        font-family: -apple-system,\n        system-ui, BlinkMacSystemFont, \\\"Segoe UI\\\", \\\"Open Sans\\\", \\\"Helvetica Neue\\\",\n        Helvetica, Arial, sans-serif;\\n        \\n    }\\n    div {\\n        width:\n        600px;\\n        margin: 5em auto;\\n        padding: 2em;\\n        background-color:\n        #fdfdff;\\n        border-radius: 0.5em;\\n        box-shadow: 2px 3px 7px 2px\n        rgba(0,0,0,0.02);\\n    }\\n    a:link, a:visited {\\n        color: #38488f;\\n\n        \\       text-decoration: none;\\n    }\\n    @media (max-width: 700px) {\\n        div\n        {\\n            margin: 0 auto;\\n            width: auto;\\n        }\\n    }\\n\n        \\   </style>    \\n</head>\\n\\n<body>\\n<div>\\n    <h1>Example Domain</h1>\\n\n        \\   <p>This domain is for use in illustrative examples in documents. You may\n        use this\\n    domain in literature without prior coordination or asking for\n        permission.</p>\\n    <p><a href=\\\"https://www.iana.org/domains/example\\\">More\n        information...</a></p>\\n</div>\\n</body>\\n</html>\\n\"\n    headers:\n      Age:\n      - '83814'\n      Cache-Control:\n      - max-age=604800\n      Content-Length:\n      - '648'\n      Content-Type:\n      - text/html; charset=UTF-8\n      Date:\n      - Sat, 07 Jan 2023 07:50:26 GMT\n      Etag:\n      - '\"3147526947+ident+gzip\"'\n      Expires:\n      - Sat, 14 Jan 2023 07:50:26 GMT\n      Last-Modified:\n      - Thu, 17 Oct 2019 07:18:26 GMT\n      Server:\n      - ECS (sab/56BA)\n      Vary:\n      - Accept-Encoding\n      X-Cache:\n      - HIT\n    status:\n      code: 200\n      message: OK\nversion: 1\n"
  },
  {
    "path": "tests/cassettes/test_buku/test_fetch_data_with_url[http---example.com-page1.txt-exp_res2].yaml",
    "content": "interactions:\n- request:\n    body: null\n    headers:\n      Accept:\n      - '*/*'\n      Accept-Encoding:\n      - gzip,deflate\n      Cookie:\n      - ''\n      DNT:\n      - '1'\n      User-Agent:\n      - Mozilla/5.0 (X11; Linux x86_64; rv:101.0) Gecko/20100101 Firefox/101.0\n    method: HEAD\n    uri: http://example.com/page1.txt\n  response:\n    body:\n      string: ''\n    headers:\n      Cache-Control:\n      - max-age=604800\n      Content-Encoding:\n      - gzip\n      Content-Length:\n      - '648'\n      Content-Type:\n      - text/html; charset=UTF-8\n      Date:\n      - Sat, 07 Jan 2023 07:50:27 GMT\n      Expires:\n      - Sat, 14 Jan 2023 07:50:27 GMT\n      Server:\n      - EOS (vny/0451)\n      Vary:\n      - Accept-Encoding\n    status:\n      code: 404\n      message: Not Found\nversion: 1\n"
  },
  {
    "path": "tests/cassettes/test_buku/test_fetch_data_with_url[http---www.vim.org-scripts-script.php~-exp_res7].yaml",
    "content": "interactions:\n- request:\n    body: null\n    headers:\n      Accept:\n      - '*/*'\n      Accept-Encoding:\n      - gzip,deflate\n      Cookie:\n      - ''\n      DNT:\n      - '1'\n      User-Agent:\n      - Mozilla/5.0 (X11; Linux x86_64; rv:101.0) Gecko/20100101 Firefox/101.0\n    method: GET\n    uri: http://www.vim.org/scripts/script.php?script_id=4641\n  response:\n    body:\n      string: '<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n\n        <html><head>\n\n        <title>302 Found</title>\n\n        </head><body>\n\n        <h1>Found</h1>\n\n        <p>The document has moved <a href=\"https://www.vim.org/scripts/script.php?script_id=4641\">here</a>.</p>\n\n        <hr>\n\n        <address>Apache/2.4.10 (Debian) Server at www.vim.org Port 80</address>\n\n        </body></html>\n\n        '\n    headers:\n      Content-Length:\n      - '314'\n      Content-Type:\n      - text/html; charset=iso-8859-1\n      Date:\n      - Sat, 07 Jan 2023 07:50:29 GMT\n      Location:\n      - https://www.vim.org/scripts/script.php?script_id=4641\n      Server:\n      - Apache/2.4.10 (Debian)\n    status:\n      code: 302\n      message: Found\n- request:\n    body: null\n    headers:\n      Accept:\n      - '*/*'\n      Accept-Encoding:\n      - gzip,deflate\n      Cookie:\n      - ''\n      DNT:\n      - '1'\n      User-Agent:\n      - Mozilla/5.0 (X11; Linux x86_64; rv:101.0) Gecko/20100101 Firefox/101.0\n    method: GET\n    uri: https://www.vim.org/scripts/script.php?script_id=4641\n  response:\n    body:\n      string: !!binary |\n        PCFET0NUWVBFIEhUTUwgUFVCTElDICItLy9XM0MvL0RURCBIVE1MIDQuMDEgVHJhbnNpdGlvbmFs\n        Ly9FTiI+CjxodG1sPgoKPGhlYWQ+CiAgPGxpbmsgcmVsPSJTdHlsZXNoZWV0IiB0eXBlPSJ0ZXh0\n        L2NzcyIgaHJlZj0iL2Nzcy9zdHlsZS5jc3MiID4KICA8dGl0bGU+bWxlc3NuYXVfY2FzZSAtICJp\n        bi1jYXNlIiBzZWxlY3Rpb24sIGRlbGV0aW9uIGFuZCBzdWJzdGl0dXRpb24gZm9yIHVuZGVyc2Nv\n        cmUsIGNhbWVsLCBtaXhlZCBjYXNlIDogdmltIG9ubGluZTwvdGl0bGU+CiAgPG1ldGEgaHR0cC1l\n        cXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50PSJ0ZXh0L2h0bWw7IGNoYXJzZXQ9SVNPLTg4NTkt\n        MSI+CiAgPG1ldGEgbmFtZT0iS0VZV09SRFMiIGNvbnRlbnQ9IlZpbSwgVmkgSU1wcm92ZWQsIHRl\n        eHQgZWRpdG9yLCBob21lLCBkb2N1bWVudGF0aW9uLCB0aXBzLCBzY3JpcHRzLCBuZXdzIj4KICA8\n        bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9MTAwMCwgaW5pdGlhbC1zY2FsZT0x\n        Ij4KICA8bGluayByZWw9InNob3J0Y3V0IGljb24iIHR5cGU9ImltYWdlL3gtaWNvbiIgaHJlZj0i\n        L2ltYWdlcy92aW1fc2hvcnRjdXQuaWNvIj4KPC9oZWFkPgoKPGJvZHkgdG9wbWFyZ2luPSIwIiBs\n        ZWZ0bWFyZ2luPSIwIiBtYXJnaW5oZWlnaHQ9IjAiIG1hcmdpbndpZHRoPSIwIiBiZ2NvbG9yPSIj\n        ZmZmZmZmIj4gCgo8IS0tIEhFQURFUiwgU1BPTlNPUiBJTUFHRSwgVklNIElNQUdFIEFORCBCT09L\n        IEFEIC0tPgo8dGFibGUgd2lkdGg9IjEwMCUiIGNlbGxwYWRkaW5nPSIwIiBjZWxsc3BhY2luZz0i\n        MCIgYm9yZGVyPSIwIj4KICA8dHI+CiAgICA8dGQgY2xhc3M9ImxpZ2h0YmciIHdpZHRoPSIyMDgi\n        IHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiAjMDA1N2I3Ij48aW1nIHNyYz0iL2ltYWdlcy9zcGFj\n        ZXIuZ2lmIiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjUiIGFsdD0iIj48L3RkPgogICAgPHRkIGNvbHNw\n        YW49IjQiIGNsYXNzPSJsaWdodGJnIj48aW1nIHNyYz0iL2ltYWdlcy9zcGFjZXIuZ2lmIiB3aWR0\n        aD0iMSIgaGVpZ2h0PSI1IiBhbHQ9IiI+PC90ZD4KICAgIDx0ZCBjbGFzcz0ibGlnaHRiZyIgd2lk\n        dGg9IjIwOCIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICMwMDU3YjciPjxpbWcgc3JjPSIvaW1h\n        Z2VzL3NwYWNlci5naWYiIHdpZHRoPSIxMDAiIGhlaWdodD0iNSIgYWx0PSIiPjwvdGQ+CiAgPC90\n        cj4KICA8dHI+CiAgPHRkPgogICAgPHRhYmxlIHdpZHRoPSIyMDgiIGNlbGxwYWRkaW5nPSIwIiBj\n        ZWxsc3BhY2luZz0iMCIgYm9yZGVyPSIwIj4KICAgICAgPHRyPgoJPHRkIGNsYXNzPSJsaWdodGJn\n        IiBzdHlsZT0iYmFja2dyb3VuZC1jb2xvcjogIzAwNTdiNyI+CgkgIDxhIGhyZWY9Imh0dHBzOi8v\n        ZXUtc29saWRhcml0eS11a3JhaW5lLmVjLmV1cm9wYS5ldS9ldS1zdGFuZHMtdWtyYWluZV9lbiI+\n        PGltZyBzcmM9Ii9pbWFnZXMvc3BhY2VyLmdpZiIgd2lkdGg9IjIwNCIgaGVpZ2h0PSIyMyIgYWx0\n        PSIiPjwvYT4KCTwvdGQ+CiAgICAgIDwvdHI+CiAgICAgIDx0cj4KCTx0ZCBjbGFzcz0ibGlnaHRi\n        ZyIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICNmZmQ3MDAiPgoJICA8YSBocmVmPSJodHRwczov\n        L2V1LXNvbGlkYXJpdHktdWtyYWluZS5lYy5ldXJvcGEuZXUvZXUtc3RhbmRzLXVrcmFpbmVfZW4i\n        PjxpbWcgc3JjPSIvaW1hZ2VzL3NwYWNlci5naWYiIHdpZHRoPSIyMDQiIGhlaWdodD0iMjMiIGFs\n        dD0iIj48L2E+CiAgICAgICAgPC90ZD4KICAgICAgPC90cj4KICAgIDwvdGFibGU+CiAgPC90ZD4K\n        ICA8dGQgY2xhc3M9ImxpZ2h0YmciPiZuYnNwOyZuYnNwOyZuYnNwOzwvdGQ+CiAgPHRkIGNsYXNz\n        PSJsaWdodGJnIiBhbGlnbj0ibGVmdCI+PGEgaHJlZj0iaHR0cHM6Ly93d3cudmltLm9yZy9zcG9u\n        c29yL2luZGV4LnBocCI+PGltZyBzcmM9Ii9pbWFnZXMvc3BvbnNvcnZpbS5naWYiIGFsdD0ic3Bv\n        bnNvciBWaW0gZGV2ZWxvcG1lbnQiIGJvcmRlcj0iMCI+PC9hPjwvdGQ+CiAgPHRkIGNsYXNzPSJs\n        aWdodGJnIiBhbGlnbj0iY2VudGVyIj4KCSA8YSBocmVmPSIvIj48aW1nIHNyYz0iL2ltYWdlcy92\n        aW1faGVhZGVyLmdpZiIgYm9yZGVyPSIwIiBhbHQ9IlZpbSBsb2dvIiBjbGFzcz0iYWxpZ24tbWlk\n        ZGxlIj48L2E+CgkgIDwvdGQ+CiAgPHRkIGNsYXNzPSJsaWdodGJnIiBhbGlnbj0icmlnaHQiPjxh\n        IGhyZWY9Imh0dHA6Ly9pY2NmLWhvbGxhbmQub3JnL2NsaWNrNS5odG1sIj48aW1nIHNyYz0iL2lt\n        YWdlcy9idXloZWxwbGVhcm4uZ2lmIiBhbHQ9IlZpbSBCb29rIEFkIiBib3JkZXI9IjAiPjwvYT48\n        L3RkPgogIDx0ZD4KICAgIDx0YWJsZSB3aWR0aD0iMjA4IiBjZWxscGFkZGluZz0iMCIgY2VsbHNw\n        YWNpbmc9IjAiIGJvcmRlcj0iMCI+CiAgICAgIDx0cj4KCTx0ZCBjbGFzcz0ibGlnaHRiZyIgd2lk\n        dGg9IjIwOCIgc3R5bGU9ImJhY2tncm91bmQtY29sb3I6ICMwMDU3YjciPgoJICA8YSBocmVmPSJo\n        dHRwczovL2V1LXNvbGlkYXJpdHktdWtyYWluZS5lYy5ldXJvcGEuZXUvZXUtc3RhbmRzLXVrcmFp\n        bmVfZW4iPjxpbWcgc3JjPSIvaW1hZ2VzL3NwYWNlci5naWYiIHdpZHRoPSIyMDQiIGhlaWdodD0i\n        MjMiIGFsdD0iIj48L2E+Cgk8L3RkPgogICAgICA8L3RyPgogICAgICA8dHI+Cgk8dGQgY2xhc3M9\n        ImxpZ2h0YmciIHdpZHRoPSIyMDgiIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiAjZmZkNzAwIj4K\n        CSAgPGEgaHJlZj0iaHR0cHM6Ly9ldS1zb2xpZGFyaXR5LXVrcmFpbmUuZWMuZXVyb3BhLmV1L2V1\n        LXN0YW5kcy11a3JhaW5lX2VuIj48aW1nIHNyYz0iL2ltYWdlcy9zcGFjZXIuZ2lmIiB3aWR0aD0i\n        MjA0IiBoZWlnaHQ9IjIzIiBhbHQ9IiI+PC9hPgoJPC90ZD4KICAgICAgPC90cj4KICAgIDwvdGFi\n        bGU+CiAgPC90ZD4KICA8L3RyPgogIDx0cj4KICAgIDx0ZCBjbGFzcz0ibGlnaHRiZyIgc3R5bGU9\n        ImJhY2tncm91bmQtY29sb3I6ICNmZmQ3MDAiPjxpbWcgc3JjPSIvaW1hZ2VzL3NwYWNlci5naWYi\n        IHdpZHRoPSIxMDAiIGhlaWdodD0iNSIgYWx0PSIiPjwvdGQ+CiAgICA8dGQgY29sc3Bhbj0iNCIg\n        Y2xhc3M9ImxpZ2h0YmciPjxpbWcgc3JjPSIvaW1hZ2VzL3NwYWNlci5naWYiIHdpZHRoPSIxIiBo\n        ZWlnaHQ9IjUiIGFsdD0iIj48L3RkPgogICAgPHRkIGNsYXNzPSJsaWdodGJnIiBzdHlsZT0iYmFj\n        a2dyb3VuZC1jb2xvcjogI2ZmZDcwMCI+PGltZyBzcmM9Ii9pbWFnZXMvc3BhY2VyLmdpZiIgd2lk\n        dGg9IjEwMCIgaGVpZ2h0PSI1IiBhbHQ9IiI+PC90ZD4KICA8L3RyPgo8L3RhYmxlPgo8IS0tIFRI\n        RSBQQUdFIEJPRFk6IEJFVFdFRU4gSEVBREVSIEFORCBGT09URVIgLS0+Cgo8dGFibGUgY2VsbHBh\n        ZGRpbmc9IjAiIGNlbGxzcGFjaW5nPSIwIiBib3JkZXI9IjAiIHdpZHRoPSIxMDAlIj4KICA8Y29s\n        IHdpZHRoPSIxODAiPgogIDxjb2wgd2lkdGg9IjEiPgoKICA8dHIgdmFsaWduPSJ0b3AiPgogICAg\n        PHRkIGNsYXNzPSJzaWRlYmFyIj4KICAgICAgPHRhYmxlIHdpZHRoPSIxODAiIGNlbGxwYWRkaW5n\n        PSI0IiBjZWxsc3BhY2luZz0iMCIgYm9yZGVyPSIwIj4KICAgICAgICA8dHIgdmFsaWduPSJ0b3Ai\n        PgogICAgICAgICAgPHRkIGNsYXNzPSJzaWRlYmFyIj4KCjwhLS0gSU5DTFVERSBUSEUgUEFHRSBO\n        QVZJR0FUSU9OIC0tPgo8dGFibGUgd2lkdGg9IjEwMCUiIGNlbGxwYWRkaW5nPSIwIiBjZWxsc3Bh\n        Y2luZz0iMCIgYm9yZGVyPSIwIiBib3JkZXJjb2xvcj0icmVkIj4KICAgIDx0cj4KICAgICAgICA8\n        dGQ+PHNtYWxsPm5vdCBsb2dnZWQgaW4gKDxhIGhyZWY9Imh0dHBzOi8vd3d3LnZpbS5vcmcvbG9n\n        aW4ucGhwIj5sb2dpbjwvYT4pPC9zbWFsbD48L3RkPgogICAgPC90cj4KICAgIDx0cj48dGQ+Cjxz\n        bWFsbD4mbmJzcDs8L3NtYWxsPgo8Zm9ybSBhY3Rpb249Imh0dHBzOi8vd3d3Lmdvb2dsZS5jb20v\n        Y3NlIiBpZD0iY3NlLXNlYXJjaC1ib3giPgogIDxkaXY+CiAgICA8aW5wdXQgdHlwZT0iaGlkZGVu\n        IiBuYW1lPSJjeCIgdmFsdWU9InBhcnRuZXItcHViLTMwMDUyNTk5OTgyOTQ5NjI6YnZ5bmk1OWtq\n        cjEiIC8+CiAgICA8aW5wdXQgdHlwZT0iaGlkZGVuIiBuYW1lPSJpZSIgdmFsdWU9IklTTy04ODU5\n        LTEiIC8+CiAgICA8aW5wdXQgdHlwZT0idGV4dCIgbmFtZT0icSIgc2l6ZT0iMjAiIC8+CiAgICA8\n        YnI+CiAgICA8aW5wdXQgdHlwZT0ic3VibWl0IiBuYW1lPSJzYSIgdmFsdWU9IlNlYXJjaCIgLz4K\n        ICA8L2Rpdj4KPC9mb3JtPgo8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCIgc3JjPSJodHRw\n        czovL3d3dy5nb29nbGUuY29tL2Nvb3AvY3NlL2JyYW5kP2Zvcm09Y3NlLXNlYXJjaC1ib3gmYW1w\n        O2xhbmc9ZW4iPjwvc2NyaXB0PgogICAgPC90ZD48L3RyPgogICAgPHRyPgogICAgICAgIDx0ZD48\n        aW1nIHNyYz0iL2ltYWdlcy9zcGFjZXIuZ2lmIiBhbHQ9IiIgYm9yZGVyPSIwIiB3aWR0aD0iMSIg\n        aGVpZ2h0PSIxIj48L3RkPgogICAgPC90cj4KICAgIDx0cj4KICAgICAgICA8dGQgY2xhc3M9ImRh\n        cmtiZyI+PGltZyBzcmM9Ii9pbWFnZXMvc3BhY2VyLmdpZiIgYWx0PScnIGJvcmRlcj0iMCIgaGVp\n        Z2h0PSIzIj48L3RkPgogICAgPC90cj4KICAgIDx0cj4KICAgICAgICA8dGQ+PGltZyBzcmM9Ii9p\n        bWFnZXMvc3BhY2VyLmdpZiIgYWx0PSIiIGJvcmRlcj0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMiI+\n        PC90ZD4KICAgIDwvdHI+CiAgICAgICAgPHRyPgogICAgICAgICAgICA8dGQgY2xhc3M9InNpZGVi\n        YXJoZWFkZXIiPjxhIGhyZWY9Imh0dHBzOi8vd3d3LnZpbS5vcmcvIj5Ib21lPC9hPjwvdGQ+CiAg\n        ICAgICAgPC90cj4KICAgICAgICA8dHI+CiAgICAgICAgICAgIDx0ZCBjbGFzcz0ic2lkZWJhcmhl\n        YWRlciI+PGEgaHJlZj0iaHR0cHM6Ly93d3cudmltLm9yZy9zZWFyY2gucGhwIj5BZHZhbmNlZCBz\n        ZWFyY2g8L2E+PC90ZD4KICAgICAgICA8L3RyPgogICAgPHRyPgogICAgICAgIDx0ZD48aW1nIHNy\n        Yz0iL2ltYWdlcy9zcGFjZXIuZ2lmIiBhbHQ9IiIgYm9yZGVyPSIwIiB3aWR0aD0iMSIgaGVpZ2h0\n        PSI3Ij48L3RkPgogICAgPC90cj4KICAgIDx0cj4KICAgICAgICA8dGQgY2xhc3M9ImNoZWNrZXIi\n        PjxpbWcgc3JjPSIvaW1hZ2VzL3NwYWNlci5naWYiIGFsdD0nJyBib3JkZXI9IjAiIGhlaWdodD0i\n        MSI+PC90ZD4KICAgIDwvdHI+CiAgICA8dHI+CiAgICAgICAgPHRkPjxpbWcgc3JjPSIvaW1hZ2Vz\n        L3NwYWNlci5naWYiIGFsdD0iIiBib3JkZXI9IjAiIHdpZHRoPSIxIiBoZWlnaHQ9IjciPjwvdGQ+\n        CiAgICA8L3RyPgogICAgICAgIDx0cj4KICAgICAgICAgICAgPHRkIGNsYXNzPSJzaWRlYmFyaGVh\n        ZGVyIj48YSBocmVmPSJodHRwczovL3d3dy52aW0ub3JnL2Fib3V0LnBocCI+QWJvdXQgVmltPC9h\n        PjwvdGQ+CiAgICAgICAgPC90cj4KICAgICAgICA8dHI+CiAgICAgICAgICAgIDx0ZCBjbGFzcz0i\n        c2lkZWJhcmhlYWRlciI+PGEgaHJlZj0iaHR0cHM6Ly93d3cudmltLm9yZy9jb21tdW5pdHkucGhw\n        Ij5Db21tdW5pdHk8L2E+PC90ZD4KICAgICAgICA8L3RyPgogICAgICAgIDx0cj4KICAgICAgICAg\n        ICAgPHRkIGNsYXNzPSJzaWRlYmFyaGVhZGVyIj48YSBocmVmPSJodHRwczovL3d3dy52aW0ub3Jn\n        L25ld3MvbmV3cy5waHAiPk5ld3M8L2E+PC90ZD4KICAgICAgICA8L3RyPgogICAgICAgIDx0cj4K\n        ICAgICAgICAgICAgPHRkIGNsYXNzPSJzaWRlYmFyaGVhZGVyIj48YSBocmVmPSJodHRwczovL3d3\n        dy52aW0ub3JnL3Nwb25zb3IvaW5kZXgucGhwIj5TcG9uc29yaW5nPC9hPjwvdGQ+CiAgICAgICAg\n        PC90cj4KICAgICAgICA8dHI+CiAgICAgICAgICAgIDx0ZCBjbGFzcz0ic2lkZWJhcmhlYWRlciI+\n        PGEgaHJlZj0iaHR0cHM6Ly93d3cudmltLm9yZy90cml2aWEucGhwIj5Ucml2aWE8L2E+PC90ZD4K\n        ICAgICAgICA8L3RyPgogICAgICAgIDx0cj4KICAgICAgICAgICAgPHRkIGNsYXNzPSJzaWRlYmFy\n        aGVhZGVyIj48YSBocmVmPSJodHRwczovL3d3dy52aW0ub3JnL2RvY3MucGhwIj5Eb2N1bWVudGF0\n        aW9uPC9hPjwvdGQ+CiAgICAgICAgPC90cj4KICAgICAgICA8dHI+CiAgICAgICAgICAgIDx0ZCBj\n        bGFzcz0ic2lkZWJhcmhlYWRlciBkb3dubG9hZCI+PGEgaHJlZj0iaHR0cHM6Ly93d3cudmltLm9y\n        Zy9kb3dubG9hZC5waHAiPkRvd25sb2FkPC9hPjwvdGQ+CiAgICAgICAgPC90cj4KICAgIDx0cj4K\n        ICAgICAgICA8dGQ+PGltZyBzcmM9Ii9pbWFnZXMvc3BhY2VyLmdpZiIgYWx0PSIiIGJvcmRlcj0i\n        MCIgd2lkdGg9IjEiIGhlaWdodD0iNyI+PC90ZD4KICAgIDwvdHI+CiAgICA8dHI+CiAgICAgICAg\n        PHRkIGNsYXNzPSJjaGVja2VyIj48aW1nIHNyYz0iL2ltYWdlcy9zcGFjZXIuZ2lmIiBhbHQ9Jycg\n        Ym9yZGVyPSIwIiBoZWlnaHQ9IjEiPjwvdGQ+CiAgICA8L3RyPgogICAgPHRyPgogICAgICAgIDx0\n        ZD48aW1nIHNyYz0iL2ltYWdlcy9zcGFjZXIuZ2lmIiBhbHQ9IiIgYm9yZGVyPSIwIiB3aWR0aD0i\n        MSIgaGVpZ2h0PSI3Ij48L3RkPgogICAgPC90cj4KICAgICAgICA8dHI+CiAgICAgICAgICAgIDx0\n        ZCBjbGFzcz0ic2lkZWJhcmhlYWRlciI+PGEgaHJlZj0iaHR0cHM6Ly93d3cudmltLm9yZy9zY3Jp\n        cHRzL2luZGV4LnBocCI+U2NyaXB0czwvYT48L3RkPgogICAgICAgIDwvdHI+CiAgICAgICAgPHRy\n        PgogICAgICAgICAgICA8dGQgY2xhc3M9InNpZGViYXJoZWFkZXIiPjxhIGhyZWY9Imh0dHBzOi8v\n        d3d3LnZpbS5vcmcvdGlwcy9pbmRleC5waHAiPlRpcHM8L2E+PC90ZD4KICAgICAgICA8L3RyPgog\n        ICAgICAgIDx0cj4KICAgICAgICAgICAgPHRkIGNsYXNzPSJzaWRlYmFyaGVhZGVyIj48YSBocmVm\n        PSJodHRwczovL3d3dy52aW0ub3JnL2FjY291bnQvaW5kZXgucGhwIj5NeSBBY2NvdW50PC9hPjwv\n        dGQ+CiAgICAgICAgPC90cj4KICAgIDx0cj4KICAgICAgICA8dGQ+PGltZyBzcmM9Ii9pbWFnZXMv\n        c3BhY2VyLmdpZiIgYWx0PSIiIGJvcmRlcj0iMCIgd2lkdGg9IjEiIGhlaWdodD0iNyI+PC90ZD4K\n        ICAgIDwvdHI+CiAgICA8dHI+CiAgICAgICAgPHRkIGNsYXNzPSJjaGVja2VyIj48aW1nIHNyYz0i\n        L2ltYWdlcy9zcGFjZXIuZ2lmIiBhbHQ9JycgYm9yZGVyPSIwIiBoZWlnaHQ9IjEiPjwvdGQ+CiAg\n        ICA8L3RyPgogICAgPHRyPgogICAgICAgIDx0ZD48aW1nIHNyYz0iL2ltYWdlcy9zcGFjZXIuZ2lm\n        IiBhbHQ9IiIgYm9yZGVyPSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSI3Ij48L3RkPgogICAgPC90cj4K\n        ICAgICAgICA8dHI+CiAgICAgICAgICAgIDx0ZCBjbGFzcz0ic2lkZWJhcmhlYWRlciI+PGEgaHJl\n        Zj0iaHR0cHM6Ly93d3cudmltLm9yZy9odWgucGhwIj5TaXRlIEhlbHA8L2E+PC90ZD4KICAgICAg\n        ICA8L3RyPgo8L3RhYmxlPgo8YnI+CjxnOnBsdXNvbmU+PC9nOnBsdXNvbmU+CgogICAgICAgICAg\n        ICA8dGFibGUgd2lkdGg9IjE3MiIgY2VsbHBhZGRpbmc9IjAiIGNlbGxzcGFjaW5nPSIwIiBib3Jk\n        ZXI9IjAiPgogICAgICAgICAgICAgIDx0cj48dGQ+PGltZyBzcmM9Ii9pbWFnZXMvc3BhY2VyLmdp\n        ZiIgYWx0PSIiIGJvcmRlcj0iMCIgd2lkdGg9IjEiIGhlaWdodD0iOCI+PC90ZD48L3RyPgogICAg\n        ICAgICAgICAgIDx0cj48dGQgY2xhc3M9ImRhcmtiZyI+PGltZyBzcmM9Ii9pbWFnZXMvc3BhY2Vy\n        LmdpZiIgd2lkdGg9IjEiIGhlaWdodD0iMyIgYWx0PSIiPjwvdGQ+PC90cj4KICAgICAgICAgICAg\n        PC90YWJsZT4KICAgICAgICAgICAgPGJyPgoKPCEtLSBJTkNMVURFIFRIRSBQQUdFIFNJREVCQVIg\n        VEVYVCAtLT4KJm5ic3A7CgogICAgICAgICAgPC90ZD4KICAgICAgICA8L3RyPgogICAgICA8L3Rh\n        YmxlPgogICAgPC90ZD4KCiAgICA8dGQgY2xhc3M9ImRhcmtiZyI+PGltZyBzcmM9Ii9pbWFnZXMv\n        c3BhY2VyLmdpZiIgd2lkdGg9IjEiIGhlaWdodD0iMSIgYm9yZGVyPSIwIiBhbHQ9IiI+PGJyPjwv\n        dGQ+CiAgICA8dGQ+CiAgICAgIDx0YWJsZSB3aWR0aD0iMTAwJSIgY2VsbHBhZGRpbmc9IjEwIiBj\n        ZWxsc3BhY2luZz0iMCIgYm9yZGVyPSIwIiBib3JkZXJjb2xvcj0icmVkIj4KICAgICAgICA8dHI+\n        CiAgICAgICAgICA8dGQgdmFsaWduPSJ0b3AiPgoKPHNwYW4gY2xhc3M9InR4dGgxIj5tbGVzc25h\n        dV9jYXNlIDogJnF1b3Q7aW4tY2FzZSZxdW90OyBzZWxlY3Rpb24sIGRlbGV0aW9uIGFuZCBzdWJz\n        dGl0dXRpb24gZm9yIHVuZGVyc2NvcmUsIGNhbWVsLCBtaXhlZCBjYXNlPC9zcGFuPiAKCjxicj4K\n        PGJyPgoKPCEtLSBrYXJtYSB0YWJsZSAtLT4KPHRhYmxlIGNlbGxwYWRkaW5nPSI0IiBjZWxsc3Bh\n        Y2luZz0iMCIgYm9yZGVyPSIxIiBib3JkZXJjb2xvcj0iIzAwMDA2NiI+Cjx0cj4KICA8dGQgY2xh\n        c3M9ImxpZ2h0YmciPjxiPiZuYnNwO3NjcmlwdCBrYXJtYSZuYnNwOzwvYj48L3RkPgogIDx0ZD4K\n        ICAgIFJhdGluZyA8Yj4tMS8xPC9iPiwKICAgIERvd25sb2FkZWQgYnkgMTYzNCAgPC90ZD4KICA8\n        dGQgY2xhc3M9ImxpZ2h0YmciPgogIDxiPiZuYnNwO0NvbW1lbnRzLCBidWdzLCBpbXByb3ZlbWVu\n        dHMmbmJzcDs8L2I+CiAgPC90ZD4KICA8dGQ+CiAgICA8YSBocmVmPSJodHRwOi8vdmltLndpa2lh\n        LmNvbS93aWtpL1NjcmlwdDo0NjQxIj5WaW0gd2lraTwvYT4KICA8L3RkPiAgCjwvdHI+CjwvdGFi\n        bGU+CjxwPgoKPHRhYmxlIGNlbGxzcGFjaW5nPSIwIiBjZWxscGFkZGluZz0iMCIgYm9yZGVyPSIw\n        Ij4KPHRyPjx0ZCBjbGFzcz0icHJvbXB0Ij5jcmVhdGVkIGJ5PC90ZD48L3RyPgo8dHI+PHRkPjxh\n        IGhyZWY9Ii9hY2NvdW50L3Byb2ZpbGUucGhwP3VzZXJfaWQ9NjY3MzkiPk1pY2hhZWwgTGXfbmF1\n        PC9hPjwvdGQ+PC90cj4KPHRyPjx0ZD4mbmJzcDs8L3RkPjwvdHI+Cjx0cj48dGQgY2xhc3M9InBy\n        b21wdCI+c2NyaXB0IHR5cGU8L3RkPjwvdHI+Cjx0cj48dGQ+dXRpbGl0eTwvdGQ+PC90cj4KPHRy\n        Pjx0ZD4mbmJzcDs8L3RkPjwvdHI+Cjx0cj48dGQgY2xhc3M9InByb21wdCI+ZGVzY3JpcHRpb248\n        L3RkPjwvdHI+Cjx0cj48dGQ+VGhpcyBwbHVnaW4gaW50cm9kdWNlcyAzIG5ldyBWaW0gY29tbWFu\n        ZHM6DTxicj4NPGJyPjEuIHZpYyAoc2VsZWN0IGluLWNhc2UpDTxicj4yLiBkaWMgKGRlbGV0ZSBp\n        bi1jYXNlKSAtLSB0aGlzIG1hcHBpbmcgaXMgZGlzYWJsZWQgYXMgb2YgdmVyc2lvbiAwLjIgKHlv\n        dSBjYW4gZGVmaW5lIHlvdXIgb3duIGlmIHlvdSBuZWVkIHRvKQ08YnI+My4gY2ljIChjaGFuZ2Uv\n        c3Vic3RpdHV0ZSBpbi1jYXNlKQ08YnI+DTxicj5TdXBwb3J0ZWQgaWRlbnRpZmllciBjYXNlcyBh\n        cmUgJnF1b3Q7Y2FtZWwgY2FzZSZxdW90OyAoY29vbENhdCwgQ29vbENhdCksICZxdW90O3NuYWtl\n        IGNhc2UmcXVvdDsgKGdvb2RfZG9nLCBHT09EX0RPRykgYW5kIGEgbWl4IG9mIGJvdGggKF9jb29s\n        RG9nLCBHb29kX0NhdCkuDTxicj4NPGJyPkJ5IHVzaW5nIHRoZSBhYm92ZSBjb21tYW5kcyB5b3Ug\n        Y2FuIHF1aWNrbHkgc2VsZWN0LCBkZWxldGUgb3IgY2hhbmdlIHNlZ21lbnRzIG9mIGNhc2VkIGlk\n        ZW50aWZpZXJzIGRlcGVuZGluZyBvbiB3aGljaCBzZWdtZW50IHRoZSBjdXJzb3IgaXMgcG9pbnRp\n        bmcgYXQuIEZvciBleGFtcGxlIChbeF0gZGVub3RlcyB0aGUgY3Vyc29yIHBvc2l0aW9uLCBvciBz\n        ZWxlY3Rpb24pOg08YnI+DTxicj52aXc6IEZvW29dQmFyIC0mZ3Q7IFtGb29dQmFyICgtJmd0OyB2\n        aXN1YWwgbW9kZSkNPGJyPmNpdzogZm9vX1tCXWFyQmF6IC0mZ3Q7IGZvb19bXUJheiAoLSZndDsg\n        aW5zZXJ0IG1vZGUpDTxicj4NPGJyPmluIG9yZGVyIHRvIHVzZSBpbi1jYXNlIGRlbGV0aW9uIHlv\n        dSBtYXkgbWFwIHRoZSBjYWxsIHRvIERlbGV0ZUluQ2FzZSgpIGFjY29yZGluZ2x5LjwvdGQ+PC90\n        cj4KPHRyPjx0ZD4mbmJzcDs8L3RkPjwvdHI+Cjx0cj48dGQgY2xhc3M9InByb21wdCI+aW5zdGFs\n        bCBkZXRhaWxzPC90ZD48L3RyPgo8dHI+PHRkPjEuIENvcHkgdGhlIGZpbGUgaW4geW91ciBwbHVn\n        aW4gZm9sZGVyIChpLmUuIH4vLnZpbS9wbHVnaW4pDTxicj4yLiBEb25lDTxicj4NPGJyPllvdSds\n        bCBuZWVkIHRvIHJlc3RhcnQgcnVubmluZyBWaW0gaW5zdGFuY2VzIGluIG9yZGVyIHRvIGJlIGFi\n        bGUgdG8gdXNlIG9mIHRoZSBwbHVnaW4uDTxicj48L3RkPjwvdHI+Cjx0cj48dGQ+Jm5ic3A7PC90\n        ZD48L3RyPgo8L3RhYmxlPgoKPCEtLSByYXRpbmcgdGFibGUgLS0+Cjxmb3JtIG5hbWU9InJhdGlu\n        ZyIgbWV0aG9kPSJwb3N0Ij4KPGlucHV0IHR5cGU9ImhpZGRlbiIgbmFtZT0ic2NyaXB0X2lkIiB2\n        YWx1ZT0iNDY0MSI+Cjx0YWJsZSBjZWxscGFkZGluZz0iNCIgY2VsbHNwYWNpbmc9IjAiIGJvcmRl\n        cj0iMSIgYm9yZGVyY29sb3I9IiMwMDAwNjYiPgo8dHI+CiAgPHRkIGNsYXNzPSJsaWdodGJnIj48\n        Yj5yYXRlIHRoaXMgc2NyaXB0PC9iPjwvdGQ+CiAgPHRkIHZhbGlnbj0ibWlkZGxlIj4KICAgIDxp\n        bnB1dCB0eXBlPSJyYWRpbyIgbmFtZT0icmF0aW5nIiB2YWx1ZT0ibGlmZV9jaGFuZ2luZyI+TGlm\n        ZSBDaGFuZ2luZwogICAgPGlucHV0IHR5cGU9InJhZGlvIiBuYW1lPSJyYXRpbmciIHZhbHVlPSJo\n        ZWxwZnVsIj5IZWxwZnVsCiAgICA8aW5wdXQgdHlwZT0icmFkaW8iIG5hbWU9InJhdGluZyIgdmFs\n        dWU9InVuZnVsZmlsbGluZyI+VW5mdWxmaWxsaW5nJm5ic3A7CiAgICA8aW5wdXQgdHlwZT0ic3Vi\n        bWl0IiB2YWx1ZT0icmF0ZSI+CiAgPC90ZD4KPC90cj4KPC90YWJsZT4KPC9mb3JtPgo8c3BhbiBj\n        bGFzcz0idHh0aDIiPnNjcmlwdCB2ZXJzaW9uczwvc3Bhbj4gKDxhIGhyZWY9ImFkZF9zY3JpcHRf\n        dmVyc2lvbi5waHA/c2NyaXB0X2lkPTQ2NDEiPnVwbG9hZCBuZXcgdmVyc2lvbjwvYT4pCjxwPgpD\n        bGljayBvbiB0aGUgcGFja2FnZSB0byBkb3dubG9hZC4KPHA+Cgo8dGFibGUgY2VsbHNwYWNpbmc9\n        IjIiIGNlbGxwYWRkaW5nPSI0IiBib3JkZXI9IjAiIHdpZHRoPSIxMDAlIj4KPHRyIGNsYXNzPSd0\n        YWJsZWhlYWRlcic+CiAgICAgICAgPHRoIHZhbGlnbj0idG9wIj5wYWNrYWdlPC90aD4KICAgIDx0\n        aCB2YWxpZ249InRvcCI+c2NyaXB0IHZlcnNpb248L3RoPgogICAgPHRoIHZhbGlnbj0idG9wIj5k\n        YXRlPC90aD4KICAgIDx0aCB2YWxpZ249InRvcCI+VmltIHZlcnNpb248L3RoPgogICAgPHRoIHZh\n        bGlnbj0idG9wIj51c2VyPC90aD4KICAgIDx0aCB2YWxpZ249InRvcCI+cmVsZWFzZSBub3Rlczwv\n        dGg+CjwvdHI+Cjx0cj4KICAgICAgICA8dGQgY2xhc3M9InJvd29kZCIgdmFsaWduPSJ0b3AiIG5v\n        d3JhcD48YSBocmVmPSJkb3dubG9hZF9zY3JpcHQucGhwP3NyY19pZD0yMDUwNyI+bWxlc3NuYXVf\n        Y2FzZS52aW08L2E+PC90ZD4KICAgIDx0ZCBjbGFzcz0icm93b2RkIiB2YWxpZ249InRvcCIgbm93\n        cmFwPjxiPjAuMjwvYj48L3RkPgogICAgPHRkIGNsYXNzPSJyb3dvZGQiIHZhbGlnbj0idG9wIiBu\n        b3dyYXA+PGk+MjAxMy0wNy0xMTwvaT48L3RkPgogICAgPHRkIGNsYXNzPSJyb3dvZGQiIHZhbGln\n        bj0idG9wIiBub3dyYXA+Ny4wPC90ZD4KICAgIDx0ZCBjbGFzcz0icm93b2RkIiB2YWxpZ249InRv\n        cCI+PGk+PGEgaHJlZj0iL2FjY291bnQvcHJvZmlsZS5waHA/dXNlcl9pZD02NjczOSI+TWljaGFl\n        bCBMZd9uYXU8L2E+PC9pPjwvdGQ+CiAgICA8dGQgY2xhc3M9InJvd29kZCIgdmFsaWduPSJ0b3Ai\n        IHdpZHRoPSIyMDAwIj5EaXNhYmxlcyBzaG9ydGN1dCAmcXVvdDtkaWMmcXVvdDsgZm9yIGluLWNh\n        c2UgZGVsZXRpb24gYXMgaXQgYXBwZWFyZWQgdG8gY29uZmxpY3Qgd2l0aCAmcXVvdDtkZCZxdW90\n        OzwvdGQ+CjwvdHI+Cjx0cj4KICAgICAgICA8dGQgY2xhc3M9InJvd2V2ZW4iIHZhbGlnbj0idG9w\n        IiBub3dyYXA+PGEgaHJlZj0iZG93bmxvYWRfc2NyaXB0LnBocD9zcmNfaWQ9MjA0NjAiPm1sZXNz\n        bmF1X2Nhc2UudmltPC9hPjwvdGQ+CiAgICA8dGQgY2xhc3M9InJvd2V2ZW4iIHZhbGlnbj0idG9w\n        IiBub3dyYXA+PGI+MC4xPC9iPjwvdGQ+CiAgICA8dGQgY2xhc3M9InJvd2V2ZW4iIHZhbGlnbj0i\n        dG9wIiBub3dyYXA+PGk+MjAxMy0wNi0zMDwvaT48L3RkPgogICAgPHRkIGNsYXNzPSJyb3dldmVu\n        IiB2YWxpZ249InRvcCIgbm93cmFwPjcuMDwvdGQ+CiAgICA8dGQgY2xhc3M9InJvd2V2ZW4iIHZh\n        bGlnbj0idG9wIj48aT48YSBocmVmPSIvYWNjb3VudC9wcm9maWxlLnBocD91c2VyX2lkPTY2NzM5\n        Ij5NaWNoYWVsIExl325hdTwvYT48L2k+PC90ZD4KICAgIDx0ZCBjbGFzcz0icm93ZXZlbiIgdmFs\n        aWduPSJ0b3AiIHdpZHRoPSIyMDAwIj5Jbml0aWFsIHVwbG9hZDwvdGQ+CjwvdHI+CjwvdGFibGU+\n        CjxzbWFsbD5pcCB1c2VkIGZvciByYXRpbmc6IDM2Ljc0LjI0Ni4xNjc8L3NtYWxsPgo8IS0tIGZp\n        bmlzaCBvZmYgdGhlIGZyYW1ld29yayAtLT4KICAgICAgICAgIDwvdGQ+CiAgICAgICAgPC90cj4K\n        ICAgICAgPC90YWJsZT4KICAgIDwvdGQ+CgogIDwvdHI+CjwvdGFibGU+Cgo8IS0tIEVORCBPRiBU\n        SEUgUEFHRSBCT0RZOiBCRVRXRUVOIEhFQURFUiBBTkQgRk9PVEVSIC0tPgoKPHRhYmxlIHdpZHRo\n        PSIxMDAlIiBjZWxscGFkZGluZz0iMCIgY2VsbHNwYWNpbmc9IjAiIGJvcmRlcj0iMCIgYm9yZGVy\n        Y29sb3I9InJlZCI+CiAgPHRyPjx0ZCBjb2xzcGFuPSI0Ij48aW1nIHNyYz0iL2ltYWdlcy9zcGFj\n        ZXIuZ2lmIiB3aWR0aD0iMSIgaGVpZ2h0PSI1IiBhbHQ9IiI+PC90ZD48L3RyPgogIDx0cj48dGQg\n        Y29sc3Bhbj0iNCIgYmdjb2xvcj0iIzAwMDAwMCI+PGltZyBzcmM9Ii9pbWFnZXMvc3BhY2VyLmdp\n        ZiIgaGVpZ2h0PSIyIiB3aWR0aD0iMSIgYWx0PSIiPjwvdGQ+PC90cj4KICA8dHI+PHRkIGNvbHNw\n        YW49IjQiPjxpbWcgc3JjPSIvaW1hZ2VzL3NwYWNlci5naWYiIHdpZHRoPSIxIiBoZWlnaHQ9IjUi\n        IGFsdD0iIj48L3RkPjwvdHI+CiAgPHRyPgogICAgPHRkPjxpbWcgc3JjPSIvaW1hZ2VzL3NwYWNl\n        ci5naWYiIHdpZHRoPSI1IiBoZWlnaHQ9IjEiIGFsdD0iIj48L3RkPgoKICAgIDx0ZCBhbGlnbj0i\n        bGVmdCIgdmFsaWduPSJ0b3AiPjxzbWFsbD4KICAgICAgSWYgeW91IGhhdmUgcXVlc3Rpb25zIG9y\n        IHJlbWFya3MgYWJvdXQgdGhpcyBzaXRlLCB2aXNpdCB0aGUKICAgICAgPGEgaHJlZj0iaHR0cDov\n        L3ZpbW9ubGluZS5zZi5uZXQiPnZpbW9ubGluZSBkZXZlbG9wbWVudDwvYT4gcGFnZXMuCiAgICAg\n        IFBsZWFzZSB1c2UgdGhpcyBzaXRlIHJlc3BvbnNpYmx5LgogICAgICA8YnI+IAogICAgICAKICAg\n        ICAgUXVlc3Rpb25zIGFib3V0IDxhIGhyZWY9Imh0dHA6Ly93d3cudmltLm9yZy9hYm91dC5waHAi\n        PlZpbTwvYT4gc2hvdWxkIGdvCiAgICAgIHRvIHRoZSA8YSBocmVmPSJodHRwOi8vd3d3LnZpbS5v\n        cmcvbWFpbGxpc3QucGhwIj5tYWlsbGlzdDwvYT4uCiAgICAgIEhlbHAgQnJhbSA8YSBocmVmPSJo\n        dHRwOi8vaWNjZi1ob2xsYW5kLm9yZy8iPmhlbHAgVWdhbmRhPC9hPi4KICAgICAgPC9zbWFsbD4K\n        CSZuYnNwOwoJJm5ic3A7CgogICAgPC90ZD4KCiAgICA8dGQgYWxpZ249InJpZ2h0IiB2YWxpZ249\n        InRvcCI+CiAgICAgIAk8YSBocmVmPSJodHRwczovL29zZG4ubmV0L3Byb2plY3RzL3ZpbSIgcmVs\n        PSJub2ZvbGxvdyI+T1NETjwvYT4KICAgIDwvdGQ+CgogICAgPHRkPjxpbWcgc3JjPSIvaW1hZ2Vz\n        L3NwYWNlci5naWYiIHdpZHRoPSI1IiBoZWlnaHQ9IjEiIGFsdD0iIj48L3RkPgogIDwvdHI+Cgog\n        ICAgCiAgPHRyPjx0ZCBjb2xzcGFuPSI0Ij48aW1nIHNyYz0iL2ltYWdlcy9zcGFjZXIuZ2lmIiB3\n        aWR0aD0iMSIgaGVpZ2h0PSI1IiBhbHQ9IiI+PC90ZD4KICAKICA8L3RyPgo8L3RhYmxlPgoKPC9i\n        b2R5Pgo8L2h0bWw+Cgo=\n    headers:\n      Content-Language:\n      - ja\n      Content-Type:\n      - text/html\n      Date:\n      - Sat, 07 Jan 2023 07:50:30 GMT\n      Server:\n      - Apache/2.4.10 (Debian)\n      Transfer-Encoding:\n      - chunked\n    status:\n      code: 200\n      message: OK\nversion: 1\n"
  },
  {
    "path": "tests/cassettes/test_buku/test_fetch_data_with_url[https---www.google.ru-search~-exp_res6].yaml",
    "content": "interactions:\n- request:\n    body: null\n    headers:\n      Accept:\n      - '*/*'\n      Accept-Encoding:\n      - gzip,deflate\n      Cookie:\n      - ''\n      DNT:\n      - '1'\n      User-Agent:\n      - Mozilla/5.0 (X11; Linux x86_64; rv:101.0) Gecko/20100101 Firefox/101.0\n    method: GET\n    uri: https://www.google.ru/search?newwindow=1&safe=off&q=xkbcomp+alt+gr&oq=xkbcomp+alt+gr&gs_l=serp.3..33i21.28976559.28977886.0.28978017.6.6.0.0.0.0.167.668.0j5.5.0....0...1c.1.64.serp..1.2.311.06cSKPTLo18\n  response:\n    body:\n      string: \"<!doctype html><html itemscope=\\\"\\\" itemtype=\\\"http://schema.org/SearchResultsPage\\\"\\\n        \\ lang=\\\"id\\\"><head><meta charset=\\\"UTF-8\\\"><meta content=\\\"origin\\\" name=\\\"\\\n        referrer\\\"><meta content=\\\"/images/branding/googleg/1x/googleg_standard_color_128dp.png\\\"\\\n        \\ itemprop=\\\"image\\\"><title>xkbcomp alt gr - Penelusuran Google</title><script\\\n        \\ nonce=\\\"fiRsZQKlRm7AeAysGAgLGA\\\">(function(){var b=window.addEventListener;window.addEventListener=function(a,c,d){\\\"\\\n        unload\\\"!==a&&b(a,c,d)};}).call(this);(function(){window.google={kEI:'QyS5Y_SSL5HO5OUPp_mggAY',kEXPI:'31',kBL:'5roH'};google.sn='web';google.kHL='id';})();(function(){\\n\\\n        var f=this||self;var h,k=[];function l(a){for(var b;a&&(!a.getAttribute||!(b=a.getAttribute(\\\"\\\n        eid\\\")));)a=a.parentNode;return b||h}function m(a){for(var b=null;a&&(!a.getAttribute||!(b=a.getAttribute(\\\"\\\n        leid\\\")));)a=a.parentNode;return b}\\nfunction n(a,b,c,d,g){var e=\\\"\\\";c||-1!==b.search(\\\"\\\n        &ei=\\\")||(e=\\\"&ei=\\\"+l(d),-1===b.search(\\\"&lei=\\\")&&(d=m(d))&&(e+=\\\"&lei=\\\"\\\n        +d));d=\\\"\\\";!c&&f._cshid&&-1===b.search(\\\"&cshid=\\\")&&\\\"slh\\\"!==a&&(d=\\\"&cshid=\\\"\\\n        +f._cshid);c=c||\\\"/\\\"+(g||\\\"gen_204\\\")+\\\"?atyp=i&ct=\\\"+a+\\\"&cad=\\\"+b+e+\\\"\\\n        &zx=\\\"+Date.now()+d;/^http:/i.test(c)&&\\\"https:\\\"===window.location.protocol&&(google.ml&&google.ml(Error(\\\"\\\n        a\\\"),!1,{src:c,glmm:1}),c=\\\"\\\");return c};h=google.kEI;google.getEI=l;google.getLEI=m;google.ml=function(){return\\\n        \\ null};google.log=function(a,b,c,d,g){if(c=n(a,b,c,d,g)){a=new Image;var\\\n        \\ e=k.length;k[e]=a;a.onerror=a.onload=a.onabort=function(){delete k[e]};a.src=c}};google.logUrl=n;}).call(this);(function(){google.y={};google.sy=[];google.x=function(a,b){if(a)var\\\n        \\ c=a.id;else{do c=Math.random();while(google.y[c])}google.y[c]=[a,b];return!1};google.sx=function(a){google.sy.push(a)};google.lm=[];google.plm=function(a){google.lm.push.apply(google.lm,a)};google.lq=[];google.load=function(a,b,c){google.lq.push([[a],b,c])};google.loadAll=function(a,b){google.lq.push([a,b])};google.bx=!1;google.lx=function(){};}).call(this);google.f={};(function(){\\n\\\n        document.documentElement.addEventListener(\\\"submit\\\",function(b){var a;if(a=b.target){var\\\n        \\ c=a.getAttribute(\\\"data-submitfalse\\\");a=\\\"1\\\"===c||\\\"q\\\"===c&&!a.elements.q.value?!0:!1}else\\\n        \\ a=!1;a&&(b.preventDefault(),b.stopPropagation())},!0);document.documentElement.addEventListener(\\\"\\\n        click\\\",function(b){var a;a:{for(a=b.target;a&&a!==document.documentElement;a=a.parentElement)if(\\\"\\\n        A\\\"===a.tagName){a=\\\"1\\\"===a.getAttribute(\\\"data-nohref\\\");break a}a=!1}a&&b.preventDefault()},!0);}).call(this);(function(){google.hs={h:true,nhs:false,sie:false};})();(function(){google.c={ataf:false,btfi:false,cap:2000,frt:true,gecoh:true,gl:false,lhc:false,llt:false,lrt:false,raf:false,sxs:false,taf:true,taff:false,timl:false};})();(function(){\\n\\\n        var f=this||self;var g=window.performance;function h(a,b,d,c){a.addEventListener?a.addEventListener(b,d,c||!1):a.attachEvent&&a.attachEvent(\\\"\\\n        on\\\"+b,d)}function k(a,b,d,c){\\\"addEventListener\\\"in a?a.removeEventListener(b,d,c||!1):a.attachEvent&&a.detachEvent(\\\"\\\n        on\\\"+b,d)};google.c.iim=google.c.iim||{};function l(a){a&&f.google.aft(a.target)}var\\\n        \\ m;function n(){k(document.documentElement,\\\"load\\\",m,!0);k(document.documentElement,\\\"\\\n        error\\\",m,!0)};google.timers={};google.startTick=function(a){google.timers[a]={t:{start:Date.now()},e:{},m:{}}};google.tick=function(a,b,d){google.timers[a]||google.startTick(a);d=void\\\n        \\ 0!==d?d:Date.now();b instanceof Array||(b=[b]);for(var c=0,e;e=b[c++];)google.timers[a].t[e]=d};google.c.e=function(a,b,d){google.timers[a].e[b]=d};google.c.b=function(a,b){b=google.timers[b||\\\"\\\n        load\\\"].m;b[a]&&google.ml(Error(\\\"a\\\"),!1,{m:a});b[a]=!0};google.c.u=function(a,b){var\\\n        \\ d=google.timers[b||\\\"load\\\"],c=d.m;if(c[a]){c[a]=!1;for(a in c)if(c[a])return;google.csiReport(d,\\\"\\\n        load2\\\"===b?\\\"all2\\\":\\\"all\\\")}else{b=\\\"\\\";for(var e in c)b+=e+\\\":\\\"+c[e]+\\\"\\\n        ;\\\";google.ml(Error(\\\"b\\\"),!1,{m:a,b:!1===c[a],s:b})}};google.rll=function(a,b,d){function\\\n        \\ c(e){d(e);k(a,\\\"load\\\",c);k(a,\\\"error\\\",c)}h(a,\\\"load\\\",c);b&&h(a,\\\"error\\\"\\\n        ,c)};f.google.aft=function(a){a.setAttribute(\\\"data-iml\\\",String(Date.now()))};google.startTick(\\\"\\\n        load\\\");var p=google.timers.load;a:{var q=p.t;if(g){var r=g.timing;if(r){var\\\n        \\ t=r.navigationStart,u=r.responseStart;if(u>t&&u<=q.start){q.start=u;p.wsrt=u-t;break\\\n        \\ a}}g.now&&(p.wsrt=Math.floor(g.now()))}}google.c.b(\\\"pr\\\",\\\"load\\\");google.c.b(\\\"\\\n        xe\\\",\\\"load\\\");function v(a){if(\\\"hidden\\\"===document.visibilityState){google.c.fh=a;var\\\n        \\ b;window.performance&&window.performance.timing&&(b=Math.floor(window.performance.timing.navigationStart+a));google.tick(\\\"\\\n        load\\\",\\\"fht\\\",b);return!0}return!1}\\nfunction w(a){v(a.timeStamp)&&k(document,\\\"\\\n        visibilitychange\\\",w,!0)}google.c.fh=Infinity;h(document,\\\"visibilitychange\\\"\\\n        ,w,!0);v(0);google.c.gl&&(m=l,h(document.documentElement,\\\"load\\\",m,!0),google.c.glu=n);}).call(this);(function(){function\\\n        \\ k(a){try{a()}catch(b){google.ml(b,!1)}}google.caft=function(a,b){null===google.aftq?k(a):(google.aftq=google.aftq||[],google.aftq.push(a),b&&window.setTimeout(function(){google.aftq&&(google.aftq=google.aftq.filter(function(c){return\\\n        \\ a!==c}),k(a))},b))};function l(){return window.performance&&window.performance.navigation&&window.performance.navigation.type};function\\\n        \\ p(a,b,c){if(!a||r(a))return 0;if(!a.getBoundingClientRect)return 1;var d=function(e){return\\\n        \\ e.getBoundingClientRect()};return t(a,b,d,c)?0:u(a,b,d)}function t(a,b,c,d){a:{for(var\\\n        \\ e=a;e&&e!==b;e=e.parentElement)if(\\\"hidden\\\"===e.style.overflow||d&&\\\"G-EXPANDABLE-CONTENT\\\"\\\n        ===e.tagName&&\\\"hidden\\\"===getComputedStyle(e).getPropertyValue(\\\"overflow\\\"\\\n        )){b=e;break a}b=null}if(!b)return!1;a=c(a);c=c(b);return a.bottom<c.top||a.top>=c.bottom||a.right<c.left||a.left>=c.right}\\n\\\n        function r(a){return\\\"none\\\"===a.style.display?!0:document.defaultView&&document.defaultView.getComputedStyle?(a=document.defaultView.getComputedStyle(a),!!a&&(\\\"\\\n        hidden\\\"===a.visibility||\\\"0px\\\"===a.height&&\\\"0px\\\"===a.width)):!1}\\nfunction\\\n        \\ u(a,b,c){var d=c(a),e=d.left+window.pageXOffset,g=d.top+window.pageYOffset,n=d.width,m=d.height,f=0;if(0>=m&&0>=n)return\\\n        \\ f;var q=window.innerHeight||document.documentElement.clientHeight;0>g+m?f=2:g>=q&&(f=4);if(0>e+n||e>=(window.innerWidth||document.documentElement.clientWidth))f|=8;else\\\n        \\ if(b){for(d=d.left;a&&a!==b;a=a.parentElement)d+=a.scrollLeft;b=c(b);if(d+n<b.left||d>=b.right)f|=8}f||(f=1,g+m>q&&(f|=4));return\\\n        \\ f};var v=window.location,w=\\\"aft afti aftr afts cbs cbt fht frt hct prt\\\n        \\ sct\\\".split(\\\" \\\");function x(a){return(a=v.search.match(new RegExp(\\\"[?&]\\\"\\\n        +a+\\\"=(\\\\\\\\d+)\\\")))?Number(a[1]):-1}\\nfunction y(a,b){var c=google.timers[b||\\\"\\\n        load\\\"];b=c.m;if(!b||!b.prs){var d=l()?0:x(\\\"qsubts\\\");0<d&&(b=x(\\\"fbts\\\"\\\n        ),0<b&&(c.t.start=Math.max(d,b)));var e=c.t,g=e.start;b={wsrt:c.wsrt||0};if(g)for(var\\\n        \\ n=0,m;m=w[n++];){var f=e[m];f&&(b[m]=Math.max(f-g,0))}0<d&&(b.gsasrt=c.t.start-d);c=c.e;a=\\\"\\\n        /gen_204?s=\\\"+google.sn+\\\"&t=\\\"+a+\\\"&atyp=csi&ei=\\\"+google.kEI+\\\"&rt=\\\";d=\\\"\\\n        \\\";for(h in b)a+=\\\"\\\"+d+h+\\\".\\\"+b[h],d=\\\",\\\";for(var q in c)a+=\\\"&\\\"+q+\\\"\\\n        =\\\"+c[q];window._cshid&&(a+=\\\"&cshid=\\\"+window._cshid);2===l()&&(a+=\\\"&bb=1\\\"\\\n        );1===l()&&(a+=\\n\\\"&r=1\\\");if(\\\"gsasrt\\\"in b){var h=x(\\\"qsd\\\");0<h&&(a+=\\\"\\\n        &qsd=\\\"+h)}google.kBL&&(a+=\\\"&bl=\\\"+google.kBL);h=a;navigator.sendBeacon?navigator.sendBeacon(h,\\\"\\\n        \\\"):google.log(\\\"\\\",\\\"\\\",h)}};function z(a){a&&google.tick(\\\"load\\\",\\\"cbs\\\"\\\n        ,a);google.tick(\\\"load\\\",\\\"cbt\\\");y(\\\"cap\\\")};var A=\\\"src bsrc url ll image\\\n        \\ img-url\\\".split(\\\" \\\");function B(a){for(var b=0;b<A.length;++b)if(a.getAttribute(\\\"\\\n        data-\\\"+A[b]))return!0;return!1}function C(a){for(var b=a;b&&\\\"center_col\\\"\\\n        !==b.id;)b=b.parentElement;var c=a.parentElement;if(c&&(\\\"G-IMG\\\"===c.tagName||c.classList.contains(\\\"\\\n        uhHOwf\\\"))&&(c.style.height||c.style.width)){var d=c.getBoundingClientRect(),e=a.getBoundingClientRect();if(d.height<=e.height||d.width<=e.width)a=c}var\\\n        \\ g;return p(a,b,null==(g=google.c)?void 0:g.gecoh)}\\ngoogle.c.iim=google.c.iim||{};var\\\n        \\ D=window.innerHeight||document.documentElement.clientHeight,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=!0,N=!0,O=-1,P,S=google.c.sxs?\\\"\\\n        load2\\\":\\\"load\\\";function T(a,b,c,d){var e=google.timers[S].t[a];e&&(c||d&&null!=b&&b<e)||google.tick(S,a,b)}function\\\n        \\ U(a,b,c){\\\"1\\\"===a.getAttribute(\\\"data-frt\\\")&&(T(\\\"frt\\\",c,!1,!0),++H,V());b&&(T(\\\"\\\n        aft\\\",c,!1,!0),T(\\\"afti\\\",c,!1,!0),++J,M||(O=D),V());google.c.timl&&T(\\\"iml\\\"\\\n        ,c,!1,!0);++F;a.setAttribute(\\\"data-frt\\\",\\\"0\\\");(google.c.timl||b)&&W()}\\n\\\n        function W(){var a=google.c.timl?F===E:I===J;!N&&a&&google.c.u(\\\"il\\\",S)}\\n\\\n        function V(){if(!M){var a=J===I,b=H===G;a&&b&&(google.c.e(S,\\\"ima\\\",String(I)),google.c.e(S,\\\"\\\n        imad\\\",String(K)),google.c.e(S,\\\"imac\\\",String(L)),google.c.e(S,\\\"aftp\\\",String(Math.round(O))),document.getElementsByClassName(\\\"\\\n        Ib7Efc\\\").length&&google.c.e(S,\\\"ddl\\\",\\\"1\\\"),P&&clearTimeout(P),y(google.c.sxs?\\\"\\\n        aft2\\\":\\\"aft\\\",S));\\\"hidden\\\"===document.visibilityState&&google.c.e(S,\\\"\\\n        hddn\\\",\\\"1\\\");if(!google.c.sxs&&null!==google.aftq&&(2===google.fevent||3===google.fevent?google.fevent:1)&((a?1:0)|(b?2:0))){google.tick(\\\"\\\n        load\\\",\\\"aftqf\\\",Date.now());var c;for(a=0;b=null==(c=google.aftq)?void 0:c[a++];)k(b);google.aftq=null}}}function\\\n        \\ X(a,b){0===b||b&8||(a.setAttribute(\\\"data-frt\\\",\\\"1\\\"),++G)}if(0<google.c.cap&&!google.c.sxs)a:{var\\\n        \\ aa=google.c.cap;if(window.performance&&window.performance.timing&&\\\"navigationStart\\\"\\\n        in window.performance.timing){var Y=window.performance.now(),Z=aa-Y;if(0<Z){P=setTimeout(z,Z,Math.floor(window.performance.timing.navigationStart+Y));break\\\n        \\ a}z()}P=void 0}google.c.wh=Math.floor(window.innerHeight||document.documentElement.clientHeight);google.c.e(S,\\\"\\\n        wh\\\",String(google.c.wh));google.c.b(\\\"il\\\",S);google.c.setup=function(a,b,c){var\\\n        \\ d=a.getAttribute(\\\"data-atf\\\");if(d)return c=Number(d),b&&!a.hasAttribute(\\\"\\\n        data-frt\\\")&&X(a,c),c;var e=\\\"string\\\"!==typeof a.src||!a.src,g=!!a.getAttribute(\\\"\\\n        data-bsrc\\\"),n=!!a.getAttribute(\\\"data-deferred\\\"),m=!n&&B(a);m&&a.setAttribute(\\\"\\\n        data-lzy_\\\",\\\"1\\\");d=C(a);a.setAttribute(\\\"data-atf\\\",String(d));var f=!!(d&1);e=(e||a.complete)&&!n&&!g&&!(f&&m);g=!google.c.lhc&&Number(a.getAttribute(\\\"\\\n        data-iml\\\"))||0;++E;if(e&&!g||a.hasAttribute(\\\"data-noaft\\\"))a.setAttribute(\\\"\\\n        data-frt\\\",\\\"0\\\"),++F,f&&++L;else{var q=d&4,h=google.c.btfi&&q&&g&&O<D;if(h){var\\\n        \\ Q=a.getBoundingClientRect().top+window.pageYOffset;!c||0>c||Q<c?O=f?D:Q:h=!1}f&&(++I,n&&++K);b&&X(a,d);h&&(T(\\\"\\\n        aft\\\",g,!1,!0),T(\\\"aftb\\\",g,!1,!0));if(e&&g)U(a,f,google.c.btfi?0:g);else{f&&(!(google.c.taff&&google.c.taf||c)||q||c&&(0>c||c>=D))&&(O=D);var\\\n        \\ R=a.src;google.rll(a,!0,function(){(n||m)&&R&&R===a.src?google.rll(a,!0,function(){U(a,f,Date.now())}):U(a,f,Date.now())})}}return\\\n        \\ d};google.c.ubr=function(a,b,c,d){google.c.taf&&O<D?(O=c||-1,T(\\\"aft\\\",b)):0>O&&(c&&(O=c),google.c.btfi&&T(\\\"\\\n        aft\\\",b));a||T(\\\"afts\\\",b,!0);d||(T(\\\"aft\\\",b,!0),M&&!google.c.frt&&(M=!1,V()),a&&N&&(T(\\\"\\\n        prt\\\",b),google.c.timl&&T(\\\"iml\\\",b,!0),N=!1,W(),google.c.setup=function(){return\\\n        \\ 0},google.c.ubr=function(){}))};}).call(this);(function(){var b=[function(){google.tick&&google.tick(\\\"\\\n        load\\\",\\\"dcl\\\")}];google.dclc=function(a){b.length?b.push(a):a()};function\\\n        \\ c(){for(var a=b.shift();a;)a(),a=b.shift()}window.addEventListener?(document.addEventListener(\\\"\\\n        DOMContentLoaded\\\",c,!1),window.addEventListener(\\\"load\\\",c,!1)):window.attachEvent&&window.attachEvent(\\\"\\\n        onload\\\",c);}).call(this);(function(){var b=[];google.jsc={xx:b,x:function(a){b.push(a)},mm:[],m:function(a){google.jsc.mm.length||(google.jsc.mm=a)}};}).call(this);(function(){\\n\\\n        var e=this||self;\\nvar f={};function w(a,c){if(null===c)return!1;if(\\\"contains\\\"\\\n        in a&&1==c.nodeType)return a.contains(c);if(\\\"compareDocumentPosition\\\"in\\\n        \\ a)return a==c||!!(a.compareDocumentPosition(c)&16);for(;c&&a!=c;)c=c.parentNode;return\\\n        \\ c==a};\\nvar y=function(a,c){return function(d){d||(d=window.event);return\\\n        \\ c.call(a,d)}},z=\\\"undefined\\\"!=typeof navigator&&/Macintosh/.test(navigator.userAgent),E=function(){this._mouseEventsPrevented=!0};var\\\n        \\ F=function(a){this.g=a;this.h=[]},G=function(a){for(var c=0;c<a.h.length;++c){var\\\n        \\ d=a.g,b=a.h[c];d.removeEventListener?d.removeEventListener(b.eventType,b.o,b.capture):d.detachEvent&&d.detachEvent(\\\"\\\n        on\\\"+b.eventType,b.o)}a.h=[]};var H=e._jsa||{};H._cfc=void 0;H._aeh=void 0;\\n\\\n        var I=function(){this.h=this.g=null},K=function(a,c){var d=J;d.g=a;d.h=c;return\\\n        \\ d};I.prototype.i=function(){var a=this.g;this.g&&this.g!=this.h?this.g=this.g.__owner||this.g.parentNode:this.g=null;return\\\n        \\ a};var L=function(){var a;this.j=a=void 0===a?[]:a;this.g=0;this.h=null;this.l=!1},N=function(a,c){var\\\n        \\ d=M;d.j=a;d.g=0;d.h=c;d.l=!1;return d};L.prototype.i=function(){if(this.l)return\\\n        \\ J.i();if(this.g!=this.j.length){var a=this.j[this.g];this.g++;a!=this.h&&a&&a.__owner&&(this.l=!0,K(a.__owner,this.h));return\\\n        \\ a}return null};var J=new I,M=new L;\\nvar Q=function(){this.s=[];this.g=[];this.h=[];this.l={};this.i=null;this.j=[];P(this,\\\"\\\n        _custom\\\")},R=function(a){return String.prototype.trim?a.trim():a.replace(/^\\\\\\\n        s+/,\\\"\\\").replace(/\\\\s+$/,\\\"\\\")},ia=function(a,c){return function m(b,g){g=void\\\n        \\ 0===g?!0:g;var l=c;if(\\\"_custom\\\"==l){l=b.detail;if(!l||!l._type)return;l=l._type}var\\\n        \\ k=l;\\\"click\\\"==k&&(z&&b.metaKey||!z&&b.ctrlKey||2==b.which||null==b.which&&4==b.button||b.shiftKey)?k=\\\"\\\n        clickmod\\\":\\\"keydown\\\"==k&&!b.a11ysc&&(k=\\\"maybe_click\\\");var u=b.srcElement||b.target;l=S(k,b,u,\\\"\\\n        \\\",null);var aa=b.path?N(b.path,this):b.composedPath?N(b.composedPath(),this):K(u,this);for(var\\\n        \\ r;r=aa.i();){var h=r;var p=void 0;r=h;var q=k,ba=b;var n=r.__jsaction;if(!n){var\\\n        \\ x;n=null;\\\"getAttribute\\\"in r&&(n=r.getAttribute(\\\"jsaction\\\"));if(x=n){n=f[x];if(!n){n={};for(var\\\n        \\ A=x.split(ca),da=A?A.length:0,B=0;B<da;B++){var v=A[B];if(v){var C=v.indexOf(\\\"\\\n        :\\\"),O=-1!=C,fa=O?R(v.substr(0,C)):ea;v=O?R(v.substr(C+1)):v;n[fa]=v}}f[x]=n}r.__jsaction=n}else\\\n        \\ n=ha,r.__jsaction=n}\\\"maybe_click\\\"==q&&n.click?(p=q,q=\\\"click\\\"):\\\"clickkey\\\"\\\n        ==q?q=\\\"click\\\":\\\"click\\\"!=q||n.click||(q=\\\"clickonly\\\");p=H._cfc&&n.click?H._cfc(r,ba,n,q,p):{eventType:p?p:q,action:n[q]||\\\"\\\n        \\\",event:null,ignore:!1};l=S(p.eventType,p.event||b,u,p.action||\\\"\\\",h,l.timeStamp);if(p.ignore||p.action)break}l&&\\\"\\\n        touchend\\\"==l.eventType&&(l.event._preventMouseEvents=E);if(p&&p.action){if(\\\"\\\n        mouseenter\\\"==k||\\\"mouseleave\\\"==k||\\\"pointerenter\\\"==k||\\\"pointerleave\\\"\\\n        ==k)if(u=b.relatedTarget,!(\\\"mouseover\\\"==b.type&&\\\"mouseenter\\\"==k||\\\"mouseout\\\"\\\n        ==b.type&&\\\"mouseleave\\\"==k||\\n\\\"pointerover\\\"==b.type&&\\\"pointerenter\\\"==k||\\\"\\\n        pointerout\\\"==b.type&&\\\"pointerleave\\\"==k)||u&&(u===h||w(h,u)))l.action=\\\"\\\n        \\\",l.actionElement=null;else{k={};for(var t in b)\\\"function\\\"!==typeof b[t]&&\\\"\\\n        srcElement\\\"!==t&&\\\"target\\\"!==t&&(k[t]=b[t]);k.type=\\\"mouseover\\\"==b.type?\\\"\\\n        mouseenter\\\":\\\"mouseout\\\"==b.type?\\\"mouseleave\\\":\\\"pointerover\\\"==b.type?\\\"\\\n        pointerenter\\\":\\\"pointerleave\\\";k.target=k.srcElement=h;k.bubbles=!1;l.event=k;l.targetElement=h}}else\\\n        \\ l.action=\\\"\\\",l.actionElement=null;h=l;a.i&&!h.event.a11ysgd&&(t=S(h.eventType,h.event,h.targetElement,h.action,h.actionElement,h.timeStamp),\\\"\\\n        clickonly\\\"==t.eventType&&(t.eventType=\\\"click\\\"),a.i(t,!0));if(h.actionElement||\\\"\\\n        maybe_click\\\"==h.eventType){if(a.i){if(!h.actionElement||\\\"A\\\"!=h.actionElement.tagName||\\\"\\\n        click\\\"!=h.eventType&&\\\"clickmod\\\"!=h.eventType||(b.preventDefault?b.preventDefault():b.returnValue=!1),(b=a.i(h))&&g){m.call(this,b,!1);return}}else{if((g=e.document)&&!g.createEvent&&g.createEventObject)try{var\\\n        \\ D=g.createEventObject(b)}catch(la){D=b}else D=b;h.event=D;a.j.push(h)}H._aeh&&\\n\\\n        H._aeh(h)}}},S=function(a,c,d,b,g,m){return{eventType:a,event:c,targetElement:d,action:b,actionElement:g,timeStamp:m||Date.now()}},ja=function(a,c){return\\\n        \\ function(d){var b=a,g=c,m=!1;\\\"mouseenter\\\"==b?b=\\\"mouseover\\\":\\\"mouseleave\\\"\\\n        ==b?b=\\\"mouseout\\\":\\\"pointerenter\\\"==b?b=\\\"pointerover\\\":\\\"pointerleave\\\"\\\n        ==b&&(b=\\\"pointerout\\\");if(d.addEventListener){if(\\\"focus\\\"==b||\\\"blur\\\"==b||\\\"\\\n        error\\\"==b||\\\"load\\\"==b||\\\"toggle\\\"==b)m=!0;d.addEventListener(b,g,m)}else\\\n        \\ d.attachEvent&&(\\\"focus\\\"==b?b=\\\"focusin\\\":\\\"blur\\\"==b&&(b=\\\"focusout\\\"\\\n        ),g=y(d,g),d.attachEvent(\\\"on\\\"+b,g));return{eventType:b,o:g,capture:m}}},P=function(a,c){if(!a.l.hasOwnProperty(c)){var\\\n        \\ d=ia(a,c),b=ja(c,d);a.l[c]=d;a.s.push(b);for(d=0;d<a.g.length;++d){var g=a.g[d];g.h.push(b.call(null,g.g))}\\\"\\\n        click\\\"==c&&P(a,\\\"keydown\\\")}};Q.prototype.o=function(a){return this.l[a]};var\\\n        \\ W=function(a,c){var d=new F(c);a:{for(var b=0;b<a.g.length;b++)if(T(a.g[b].g,c)){c=!0;break\\\n        \\ a}c=!1}if(c)return a.h.push(d),d;U(a,d);a.g.push(d);V(a);return d},V=function(a){for(var\\\n        \\ c=a.h.concat(a.g),d=[],b=[],g=0;g<a.g.length;++g){var m=a.g[g];X(m,c)?(d.push(m),G(m)):b.push(m)}for(g=0;g<a.h.length;++g)m=a.h[g],X(m,c)?d.push(m):(b.push(m),U(a,m));a.g=b;a.h=d},U=function(a,c){var\\\n        \\ d=c.g;ka&&(d.style.cursor=\\\"pointer\\\");for(d=0;d<a.s.length;++d)c.h.push(a.s[d].call(null,c.g))},Y=function(a,c){a.i=c;a.j&&(0<a.j.length&&c(a.j),a.j=null)},X=function(a,c){for(var\\\n        \\ d=0;d<c.length;++d)if(c[d].g!=a.g&&T(c[d].g,a.g))return!0;return!1},T=function(a,c){for(;a!=c&&c.parentNode;)c=c.parentNode;return\\\n        \\ a==c},ka=\\\"undefined\\\"!=typeof navigator&&/iPhone|iPad|iPod/.test(navigator.userAgent),ca=/\\\\\\\n        s*;\\\\s*/,ea=\\\"click\\\",ha={};var Z=new Q;W(Z,window.document.documentElement);P(Z,\\\"\\\n        click\\\");P(Z,\\\"focus\\\");P(Z,\\\"focusin\\\");P(Z,\\\"blur\\\");P(Z,\\\"focusout\\\");P(Z,\\\"\\\n        error\\\");P(Z,\\\"load\\\");P(Z,\\\"auxclick\\\");P(Z,\\\"change\\\");P(Z,\\\"dblclick\\\"\\\n        );P(Z,\\\"beforeinput\\\");P(Z,\\\"input\\\");P(Z,\\\"keyup\\\");P(Z,\\\"keydown\\\");P(Z,\\\"\\\n        keypress\\\");P(Z,\\\"mousedown\\\");P(Z,\\\"mouseenter\\\");P(Z,\\\"mouseleave\\\");P(Z,\\\"\\\n        mouseout\\\");P(Z,\\\"mouseover\\\");P(Z,\\\"mouseup\\\");P(Z,\\\"paste\\\");P(Z,\\\"pointerenter\\\"\\\n        );P(Z,\\\"pointerleave\\\");P(Z,\\\"touchstart\\\");P(Z,\\\"touchend\\\");P(Z,\\\"touchcancel\\\"\\\n        );P(Z,\\\"transitioncancel\\\");P(Z,\\\"transitionend\\\");P(Z,\\\"transitionrun\\\");P(Z,\\\"\\\n        transitionstart\\\");P(Z,\\\"dragover\\\");P(Z,\\\"dragenter\\\");P(Z,\\\"dragleave\\\"\\\n        );P(Z,\\\"drop\\\");P(Z,\\\"dragstart\\\");P(Z,\\\"dragend\\\");P(Z,\\\"speech\\\");(function(a){google.jsad=function(c){Y(a,c)};google.jsaac=function(c){return\\\n        \\ W(a,c)};google.jsarc=function(c){G(c);for(var d=!1,b=0;b<a.g.length;++b)if(a.g[b]===c){a.g.splice(b,1);d=!0;break}if(!d)for(d=0;d<a.h.length;++d)if(a.h[d]===c){a.h.splice(d,1);break}V(a)}})(Z);e.gws_wizbind=function(a){return{trigger:function(c){var\\\n        \\ d=a.o(c.type);d||(P(a,c.type),d=a.o(c.type));var b=c.target||c.srcElement;d&&d.call(b.ownerDocument.documentElement,c)},bind:function(c){Y(a,c)}}}(Z);}).call(this);(function(){\\n\\\n        function b(c){var a;a:{for(a=c.target;a&&a!==document.documentElement;a=a.parentElement)if(\\\"\\\n        A\\\"===a.tagName&&\\\"1\\\"===a.getAttribute(\\\"data-jsarwt\\\"))break a;a=null}a&&window.jsarwt(a,null,c);return!0};window.document.documentElement.addEventListener(\\\"\\\n        mousedown\\\",b,!0);window.document.documentElement.addEventListener(\\\"touchstart\\\"\\\n        ,b,!0);}).call(this);(function(){window.rwt=function(){return!0};}).call(this);(function(){\\n\\\n        var a=this||self;function d(c){var b;a:{if(b=a.navigator)if(b=b.userAgent)break\\\n        \\ a;b=\\\"\\\"}return-1!=b.indexOf(c)};function h(){return d(\\\"Safari\\\")&&!(k()||d(\\\"\\\n        Coast\\\")||d(\\\"Opera\\\")||d(\\\"Edge\\\")||d(\\\"Edg/\\\")||d(\\\"OPR\\\")||d(\\\"Firefox\\\"\\\n        )||d(\\\"FxiOS\\\")||d(\\\"Silk\\\")||d(\\\"Android\\\"))}function k(){return(d(\\\"Chrome\\\"\\\n        )||d(\\\"CriOS\\\"))&&!d(\\\"Edge\\\")||d(\\\"Silk\\\")};var m=function(c){return String(c).replace(/\\\\\\\n        -([a-z])/g,function(b,e){return e.toUpperCase()})};var n=d(\\\"Trident\\\")||d(\\\"\\\n        MSIE\\\");!d(\\\"Android\\\")||k();k();h();var p=!n&&!h();window.jsarwt=function(c,b,e){if(!b)if(p&&c.dataset)b=c.dataset;else{b={};for(var\\\n        \\ l=c.attributes,f=0;f<l.length;++f){var g=l[f];if(0==g.name.lastIndexOf(\\\"\\\n        data-\\\",0)){var q=m(g.name.slice(5));b[q]=g.value}}}if(!(\\\"jrwt\\\"in b))if(window.rwt(c,\\\"\\\n        \\\",\\\"\\\",\\\"\\\",b.cd||\\\"\\\",b.usg||\\\"\\\",\\\"\\\",b.ved||\\\"\\\",Number(b.au)||null,b.psig||\\\"\\\n        \\\",e),p&&c.dataset)c.dataset.jrwt=\\\"1\\\";else{if(/-[a-z]/.test(\\\"jrwt\\\"))throw\\\n        \\ Error(\\\"a\\\");c.setAttribute.call(c,\\\"data-\\\"+\\\"jrwt\\\".replace(/([A-Z])/g,\\\"\\\n        -$1\\\").toLowerCase(),\\\"1\\\")}return!1};}).call(this);(function(){window._skwEvts=[];})();(function(){window.google.erd={jsr:1,bv:1719,sd:true,de:true};})();(function(){var\\\n        \\ sdo=false;var mei=10;\\nvar h=this||self;var k,l=null!=(k=h.mei)?k:1,n,p=null!=(n=h.sdo)?n:!0,q=0,r,t=google.erd,v=t.jsr;google.ml=function(a,b,d,m,e){e=void\\\n        \\ 0===e?2:e;b&&(r=a&&a.message);if(google.dl)return google.dl(a,e,d),null;if(0>v){window.console&&console.error(a,d);if(-2===v)throw\\\n        \\ a;b=!1}else b=!a||!a.message||\\\"Error loading script\\\"===a.message||q>=l&&!m?!1:!0;if(!b)return\\\n        \\ null;q++;d=d||{};b=encodeURIComponent;var c=\\\"/gen_204?atyp=i&ei=\\\"+b(google.kEI);google.kEXPI&&(c+=\\\"\\\n        &jexpid=\\\"+b(google.kEXPI));c+=\\\"&srcpg=\\\"+b(google.sn)+\\\"&jsr=\\\"+b(t.jsr)+\\\"\\\n        &bver=\\\"+b(t.bv);var f=a.lineNumber;void 0!==f&&(c+=\\\"&line=\\\"+f);var g=\\n\\\n        a.fileName;g&&(0<g.indexOf(\\\"-extension:/\\\")&&(e=3),c+=\\\"&script=\\\"+b(g),f&&g===window.location.href&&(f=document.documentElement.outerHTML.split(\\\"\\\n        \\\\n\\\")[f],c+=\\\"&cad=\\\"+b(f?f.substring(0,300):\\\"No script found.\\\")));c+=\\\"\\\n        &jsel=\\\"+e;for(var u in d)c+=\\\"&\\\",c+=b(u),c+=\\\"=\\\",c+=b(d[u]);c=c+\\\"&emsg=\\\"\\\n        +b(a.name+\\\": \\\"+a.message);c=c+\\\"&jsst=\\\"+b(a.stack||\\\"N/A\\\");12288<=c.length&&(c=c.substr(0,12288));a=c;m||google.log(0,\\\"\\\n        \\\",a);return a};window.onerror=function(a,b,d,m,e){r!==a&&(a=e instanceof\\\n        \\ Error?e:Error(a),void 0===d||\\\"lineNumber\\\"in a||(a.lineNumber=d),void 0===b||\\\"\\\n        fileName\\\"in a||(a.fileName=b),google.ml(a,!1,void 0,!1,\\\"SyntaxError\\\"===a.name||\\\"\\\n        SyntaxError\\\"===a.message.substring(0,11)||-1!==a.message.indexOf(\\\"Script\\\n        \\ error\\\")?3:0));r=null;p&&q>=l&&(window.onerror=null)};})();var h=\\\"function\\\"\\\n        ==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){if(a==Array.prototype||a==Object.prototype)return\\\n        \\ a;a[b]=c.value;return a},k=function(a){a=[\\\"object\\\"==typeof globalThis&&globalThis,a,\\\"\\\n        object\\\"==typeof window&&window,\\\"object\\\"==typeof self&&self,\\\"object\\\"==typeof\\\n        \\ global&&global];for(var b=0;b<a.length;++b){var c=a[b];if(c&&c.Math==Math)return\\\n        \\ c}throw Error(\\\"a\\\");},l=k(this),m=function(a,b){if(b)a:{var c=l;a=a.split(\\\"\\\n        .\\\");for(var d=0;d<a.length-1;d++){var e=a[d];if(!(e in\\nc))break a;c=c[e]}a=a[a.length-1];d=c[a];b=b(d);b!=d&&null!=b&&h(c,a,{configurable:!0,writable:!0,value:b})}};m(\\\"\\\n        String.prototype.startsWith\\\",function(a){return a?a:function(b,c){if(null==this)throw\\\n        \\ new TypeError(\\\"The 'this' value for String.prototype.startsWith must not\\\n        \\ be null or undefined\\\");if(b instanceof RegExp)throw new TypeError(\\\"First\\\n        \\ argument to String.prototype.startsWith must not be a regular expression\\\"\\\n        );var d=this+\\\"\\\";b+=\\\"\\\";var e=d.length,g=b.length;c=Math.max(0,Math.min(c|0,d.length));for(var\\\n        \\ f=0;f<g&&c<e;)if(d[c++]!=b[f++])return!1;return f>=g}});google.arwt=function(a){a.href=document.getElementById(a.id.substring(a.id.startsWith(\\\"\\\n        vcs\\\")?3:1)).href;return!0};(function(){\\nvar f=this||self;var g=function(a){var\\\n        \\ b=a.indexOf(\\\"#\\\");0>b&&(b=a.length);var c=a.indexOf(\\\"?\\\");if(0>c||c>b){c=b;var\\\n        \\ d=\\\"\\\"}else d=a.substring(c+1,b);return[a.slice(0,c),d,a.slice(b)]},h=function(a,b){return\\\n        \\ b?a?a+\\\"&\\\"+b:b:a},l=function(a,b,c){if(Array.isArray(b))for(var d=0;d<b.length;d++)l(a,String(b[d]),c);else\\\n        \\ null!=b&&c.push(a+(\\\"\\\"===b?\\\"\\\":\\\"=\\\"+encodeURIComponent(String(b))))},m=function(a){var\\\n        \\ b=[],c;for(c in a)l(c,a[c],b);return b.join(\\\"&\\\")},n=function(){var a=Math.floor(2147483648*Math.random()).toString(36)+Math.abs(Math.floor(2147483648*\\n\\\n        Math.random())^Date.now()).toString(36);if(a=\\\"zx\\\"+(null!=a?\\\"=\\\"+encodeURIComponent(String(a)):\\\"\\\n        \\\")){var b=g(\\\"https://pagead2.googlesyndication.com/pagead/gen_204\\\");b[1]=h(b[1],a);a=b[0]+(b[1]?\\\"\\\n        ?\\\"+b[1]:\\\"\\\")+b[2]}else a=\\\"https://pagead2.googlesyndication.com/pagead/gen_204\\\"\\\n        ;return a},p=function(){var a=n(),b={id:\\\"visibilityhiddenSRP\\\",payload:google.vcmd,clickstring:google.cstrfph};a=g(a);var\\\n        \\ c=a[1],d=[];c&&c.split(\\\"&\\\").forEach(function(e){var k=e.indexOf(\\\"=\\\"\\\n        );b.hasOwnProperty(0<=k?e.slice(0,k):e)||d.push(e)});a[1]=h(d.join(\\\"&\\\"),m(b));return\\\n        \\ a[0]+(a[1]?\\\"?\\\"+a[1]:\\\"\\\")+a[2]};function q(a){for(;a&&a!=document.documentElement;a=a.parentElement)if(\\\"\\\n        A\\\"==a.tagName)return a;return null}function r(){\\\"visible\\\"===document.visibilityState?(google.vcmd=\\\"\\\n        \\\",google.cstrfph=\\\"\\\"):\\\"hidden\\\"===document.visibilityState&&google.cstrfph&&google.vcmd&&null!=navigator&&navigator.sendBeacon(p())}function\\\n        \\ t(){google.cstrfph&&google.vcmd&&(google.vcmd+=\\\"+pagehide\\\")}\\nfunction\\\n        \\ u(a){if(a=q(a.target))switch(a.getAttribute(\\\"data-agdh\\\")){case \\\"arwt\\\"\\\n        :google.arwt(a);break;case \\\"fvd3vc\\\":f.J4LCUe(a);break;case \\\"EdKoMd\\\":(0,google.f.LmvwCb)(a)}return!0};window.document.documentElement.addEventListener(\\\"\\\n        mousedown\\\",u,!0);window.document.documentElement.addEventListener(\\\"touchstart\\\"\\\n        ,u,!0);window.document.documentElement.addEventListener(\\\"click\\\",function(a){var\\\n        \\ b=q(a.target);if(b)switch(b.getAttribute(\\\"data-agch\\\")){case \\\"ausb\\\":google.ausb(b);break;case\\\n        \\ \\\"HJ3bqe\\\":window.YvikHb(a,b);break;case \\\"cqUJI\\\":(0,google.f.DfwaCb)(b)}return!0},!0);google.eplfdd&&(window.document.addEventListener(\\\"\\\n        visibilitychange\\\",r,!0),window.addEventListener(\\\"pagehide\\\",t,!0));}).call(this);</script><style>html,body,h1,input,select{font-family:arial,sans-serif}body,h1{font-size:14px;}h1{font-weight:normal;margin:0;padding:0}h3{font-weight:normal;margin:0;padding:0;font-size:20px;line-height:1.3}body{margin:0;background:#fff;color:#202124;}a{color:#1a0dab;text-decoration:none;-webkit-tap-highlight-color:rgba(0,0,0,.1)}a:visited{color:#681da8}a:hover{text-decoration:underline}a:hover\\\n        \\ h3{text-decoration:underline}a.a-no-hover-decoration:hover,a.a-no-hover-decoration:hover\\\n        \\ h3{text-decoration:none}cite,cite a:link,cite a:visited{color:#202124;font-style:normal}button{margin:0}ol\\\n        \\ li{list-style:none}ol,ul,li{margin:0;padding:0}input{font-size:14px}input:focus{outline:none}input::-moz-focus-inner{border:0}em{font-weight:bold;font-style:normal}.aCOpRe\\\n        \\ em,.yXK7lf em{color:#5f6368;}.aCOpRe a em{color:inherit}@-webkit-keyframes\\\n        \\ qs-timer {0%{}}html:not(.zAoYTe) [tabindex]{outline:0}html:not(.zAoYTe)\\\n        \\ [href],html:not(.zAoYTe) button,html:not(.zAoYTe) iframe,html:not(.zAoYTe)\\\n        \\ input,html:not(.zAoYTe) select,html:not(.zAoYTe) textarea{outline:0}html:not(.zAoYTe)\\\n        \\ .F0azHf{outline:0}.z1asCe{display:inline-block;fill:currentColor;height:24px;line-height:24px;position:relative;width:24px}.z1asCe\\\n        \\ svg{display:block;height:100%;width:100%}.ynAwRc{color:#1a0dab}a:visited\\\n        \\ .ynAwRc,a:visited.ynAwRc{color:#681da8}.JIFdL{color:#1a0dab}.zIamNc{color:#202124;font-family:arial,sans-serif;font-size:12px;font-weight:400;line-height:20px}.oPWl9c{color:#70757a;font-family:arial,sans-serif;font-size:12px;font-weight:400;line-height:20px}.NUnG9d{color:#202124;font-family:arial,sans-serif;font-size:12px;font-weight:400;line-height:16px}.BjWz4c{color:#70757a;font-family:arial,sans-serif;font-size:12px;font-weight:400;line-height:16px}.cj1ht{color:#4d5156;font-family:arial,sans-serif;font-size:12px;font-weight:400;line-height:16px}.kqEaA{color:#70757a;font-family:arial,sans-serif;font-size:14px;font-weight:400;line-height:22px}.vJtJab{color:#1a0dab;font-family:arial,sans-serif;font-size:14px;font-weight:400;line-height:22px}.SGNhVe{font-family:Google\\\n        \\ Sans,arial,sans-serif;font-size:48px;letter-spacing:0;line-height:56px}.EX5Zne{font-family:Google\\\n        \\ Sans,arial,sans-serif;font-size:36px;line-height:40px}.JgzqYd{font-family:Google\\\n        \\ Sans,arial,sans-serif;font-size:28px;line-height:36px}.aTI8gc{font-family:Google\\\n        \\ Sans,arial,sans-serif;font-weight:500;line-height:36px;font-size:28px}.IFnjPb{font-family:Google\\\n        \\ Sans,arial,sans-serif;font-weight:400;line-height:28px;font-size:22px}.pb3iw{font-family:Google\\\n        \\ Sans,arial,sans-serif;font-size:18px;font-weight:400;line-height:24px}.ILxcde{font-family:Google\\\n        \\ Sans,arial,sans-serif;font-size:16px;font-weight:400;line-height:24px}.MBeuO{font-family:arial,sans-serif;font-size:20px;font-weight:400;line-height:24px}.tNxQIb{font-family:arial,sans-serif;font-size:16px;font-weight:400;line-height:24px}.ZwRhJd{font-family:arial,sans-serif;font-size:14px;line-height:18px}.NNMgCf{font-family:arial,sans-serif;font-size:16px;line-height:24px}.Pqkn2e{font-family:arial,sans-serif;font-size:16px;line-height:22px}.ApHyTb{font-family:arial,sans-serif;font-size:12px;line-height:16px}.k1U36b{font-size:12px}.sjVJQd{font-family:Google\\\n        \\ Sans,arial,sans-serif-medium,sans-serif;font-size:14px;font-weight:500;line-height:20px}.hWgrdb{font-style:italic}.RiJqbb{font-family:Google\\\n        \\ Sans,arial,sans-serif-medium,sans-serif;font-weight:500}.q8U8x{font-family:Google\\\n        \\ Sans,arial,sans-serif;font-weight:400}.Z5bgrc{font-family:arial,sans-serif-medium,sans-serif;font-weight:500}.BBwThe{font-weight:700}.l97dzf{font-weight:400}.N8MDs{font-family:arial,sans-serif-light,sans-serif}.z8gr9e{color:#4d5156}.RES9jf{color:#202124}.KHW3x{color:#fff}.ZYHQ7e{color:#70757a}.GS5rRd{color:#1a0dab}.GS5rRd:visited{color:#681da8}.x2sBq{color:#d93025}.tGXccd{color:#188038}.OvuNCb{color:#e37400}.yNSCTe{color:#202124}.XEI2lf{color:#fff}.u2fAP{color:#3c4043}.Q7PwXb{text-decoration:none}.AraNOb{text-decoration:underline}.OSrXXb{overflow:hidden;text-overflow:ellipsis}.cHaqb{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.NyOyWb{text-overflow:clip;overflow:hidden}.NnEaBd{text-align:right}.xLQxIf{text-transform:capitalize}.SlP8xc{text-transform:none}.uKdaQe{text-transform:lowercase}.n9iHLc{text-transform:uppercase}.MUmB9{text-transform:none}.iUh30{font-size:14px;line-height:1.3}.f{color:#70757a;line-height:1.58}.std,.g{font-family:arial,sans-serif;font-size:14px;}.g{line-height:1.58;text-align:left}.g,.KIy09e{width:600px;margin-top:0;margin-bottom:30px;}.iUh30{padding-top:1px;}.FzvWSb{margin-bottom:5px}.vk_c{position:relative;padding:20px\\\n        \\ 16px 24px;background-color:#fff;width:618px;}#rhs .fIcnad{border:none;margin-left:0}.vk_c,#rhs\\\n        \\ .fIcnad{border-radius:8px;border:1px solid #dadce0;box-shadow:none}.vk_c\\\n        \\ .vk_c{border-radius:0;box-shadow:none;background-color:transparent;border:0;box-shadow:none;margin:0;padding:0;position:static}.vkc_np{margin-left:-16px;margin-right:-16px}.WIDPrb{padding-left:16px}.iiFzhd{padding-right:16px}.vk_arc{border-top:1px\\\n        \\ solid #dadce0;cursor:pointer;height:0;margin-bottom:-19px;overflow:hidden;padding:20px\\\n        \\ 0;text-align:center}.vk_ard{top:-11px}.vk_aru{bottom:-6px}.vk_ard,.vk_aru{background-color:#ebebeb;margin-left:auto;margin-right:auto;position:relative;height:6px;width:64px}.vk_ard:after,.vk_ard:before,.vk_aru:after,.vk_aru:before{content:'\\\n        \\ ';height:0;left:0;position:absolute;width:0;border-left:32px solid rgba(255,255,255,0);border-right:32px\\\n        \\ solid rgba(255,255,255,0)}.vk_ard:before{border-top:16px solid #ebebeb;top:6px}.vk_aru:before{border-bottom:16px\\\n        \\ solid #ebebeb;bottom:6px}.vk_ard:after{top:0;border-top:16px solid #fff}.vk_aru:after{bottom:0;border-bottom:16px\\\n        \\ solid #fff}.jC7Epd.vk_ard,.jC7Epd.vk_aru{background-color:#202124}.jC7Epd.vk_ard:before{border-top-color:#202124}.jC7Epd.vk_aru:before{border-bottom-color:#202124}.xpdclps,.xpdxpnd{overflow:hidden}.xpdclps,.xpdxpnd{transition:max-height\\\n        \\ 0.3s}.xpdxpnd,.xpdopen .xpdclps,.xpdopen .xpdxpnd.xpdnoxpnd{max-height:0}.xpdopen\\\n        \\ .xpdxpnd{max-height:none}.xpdopen .xpdbox .xpdxpnd,.xpdopen .xpdbox.xpdopen\\\n        \\ .xpdclps{max-height:0}.xpdopen .xpdbox.xpdopen .xpdxpnd,.xpdopen .xpdbox\\\n        \\ .xpdclps{max-height:none}.xpdclose .k5nfEc{display:none}.fp-i .SzDvzc{display:none}.fp-f{bottom:0;height:auto;left:0;position:fixed\\\n        \\ !important;right:0;top:0;width:auto;z-index:127}.fp-h:not(.fp-nh):not(.goog-modalpopup-bg):not(.goog-modalpopup){display:none\\\n        \\ !important}.fp-zh.fp-h:not(.fp-nh):not(.goog-modalpopup-bg):not(.goog-modalpopup){display:block\\\n        \\ !important;height:0;overflow:hidden;transform:translate3d(0,0,0);transform:translate3d(0,0,0)}.fp-i\\\n        \\ .fp-c{display:block;min-height:100vh}li.fp-c{list-style:none}.fp-w{box-sizing:border-box;left:0;margin-left:auto;margin-right:auto;max-width:1217px;right:0}.ellip{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tF2Cxc{position:relative}.Jb0Zif\\\n        \\ .BDNLRc{margin:16px 16px -11px}.RUXr2d{display:inline}.MTB56{margin-right:12px;vertical-align:middle}.Pthbuf{display:flex;align-items:center}.m164Nd{vertical-align:middle;display:inline-block}.qpGQpf{clear:both;padding-top:6px}.tcPEUc\\\n        \\ .MTB56{display:none}.aCOpRe{line-height:1.58;word-wrap:break-word}.aCOpRe\\\n        \\ sup{line-height:0.9}.yuRUbf{font-weight:normal;font-size:small;line-height:1.58;}.IsZvec{max-width:48em;color:#4d5156;line-height:1.58}.uo4vr{color:#70757a;line-height:1.58}.IjZ7ze{display:inline-block;color:#70757a;font-size:12px;line-height:1.34;white-space:nowrap}.FyYA1e{margin:5px\\\n        \\ 0}.P1usbc{display:table;white-space:nowrap;margin:5px 0;line-height:1.58;color:#70757a;}.G1Rrjc{display:table-cell;padding-left:15px;vertical-align:baseline}.i4vd5e{display:table-cell}.VNLkW{display:table-row;vertical-align:top}.h7mcFf{color:#70757a}.k6DEPe{display:table-row;width:100%}.TXwUJf{color:#70757a}.PcHvNb{position:absolute}.N3nEGc{background-color:#fff;float:left;margin-top:4px}.wEQKyf.N3nEGc{float:right;margin:7px\\\n        \\ 0 5px 12px}.wEQKyf.Ik9SRc.N3nEGc{margin:2px 0 0 0}.Ixi80c{margin-top:0}.i0PvJb{background-color:#000}.mWTy7c{border-top-left-radius:2px;bottom:0;font-size:11px;padding:1px\\\n        \\ 3px;position:absolute;right:0;background-color:rgba(0,0,0,.7);color:#fff}.rGhul{display:block;position:relative;overflow:hidden}.rGhul:focus{outline-style:solid;outline-width:2px}.vYWbhc{margin-top:0}.TbwUpd\\\n        \\ a.fl{font-size:14px}.TQc1id .qLRx3b{font-size:14px;line-height:1.58}.TbwUpd{display:inline-block;padding-bottom:2px;padding-top:1px;-webkit-text-size-adjust:none}.NJjxre{position:absolute;left:0;top:0}.M8OgIe\\\n        \\ .VWCdhc.Mjve0e .TbwUpd{width:max-content}.Uo8X3b{clip:rect(1px,1px,1px,1px);height:1px;overflow:hidden;position:absolute;white-space:nowrap;width:1px;z-index:-1000;user-select:none}.OhScic{margin:0px}.zsYMMe{padding:0px}#rhs{margin-left:var(--rhs-margin);flex:0\\\n        \\ auto;width:369px;position:relative;padding-bottom:15px;transition:opacity\\\n        \\ 0.3s}#rhs .scrt.VjDLd,#rhs table.VjDLd{border:0}#rhs .VjDLd{border:1px solid\\\n        \\ #f8f9fa;padding-left:17px;padding-right:16px;position:relative;box-sizing:border-box}.s6JM6d\\\n        \\ .SwlyWb{display:none}#rhs.rhstc4 .VjDLd{width:369px}#rhs.rhstc5 .VjDLd{width:457px}.rhstc4\\\n        \\ .nmrhhd{background:none !important;display:none !important}.rhstc5 .SwlyWb{background:none\\\n        \\ !important;display:none !important}.GLcBOb{color:#70757a;font-size:14px;font-family:Google\\\n        \\ Sans,arial,sans-serif;border-bottom:1px solid #ebebeb;margin-top:-21px;position:relative;z-index:126}.IC1Ck{position:relative;white-space:nowrap;align-items:baseline;display:flex;-ms-flex-pack:justify;float:left;justify-content:space-between;min-width:calc(var(--center-abs-margin)\\\n        \\ + 652px);}.MUFPAc{display:inline;margin-left:calc(var(--center-abs-margin)\\\n        \\ + -11px);}.MbEPDb{margin-left:-4px;vertical-align:text-bottom;}.hdtb-mitem\\\n        \\ .GOE98c,.hdtb-mitem a,.hdtb-mitem.hdtb-msel,.t2vtad{color:#5f6368;text-decoration:none;display:inline-block;padding:0\\\n        \\ 12px;padding:8px 16px 8px 16px;padding:17px 12px 11px 10px;}.hdtb-mitem{height:16px;line-height:16px;margin:11px\\\n        \\ 1px 0;display:inline-block}.hdtb-mitem a:active{color:#1a73e8}.hdtb-mitem.hdtb-msel{color:#1a73e8;}.cCvmNd\\\n        \\ .hdtb-mitem.hdtb-msel{border-bottom:none}.hdtb-mitem.hdtb-msel:hover{cursor:pointer}.hdtb-mitem.hdtb-msel:active{background:none}.hdtb-mitem\\\n        \\ a{color:#5f6368}.t2vtad{border:1px solid transparent;text-align:center;border-radius:2px;line-height:19px;cursor:pointer;margin-left:-1px;padding:4px\\\n        \\ 11px;margin-right:-11px;}.t2vtad:not(.hdtb-tl-sel):hover{box-shadow:0 1px\\\n        \\ 1px rgba(0,0,0,0.1);transition:all 0.0s;background-color:#f8f9fa;background-image:linear-gradient(top,#f8f9fa,#f8f9fa);border:1px\\\n        \\ solid #dadce0;color:#202124}.t2vtad:active,.t2vtad:not(.hdtb-tl-sel):hover:active{background-color:#f8f9fa;background-image:linear-gradient(top,#f8f9fa,#f8f9fa);box-shadow:inset\\\n        \\ 0 1px 2px rgba(0,0,0,0.1)}.YTDezd{background:#1a73e8;height:3px;margin-top:11px;}.bmaJhd{margin-right:5px;vertical-align:text-bottom}.v7W49e{margin-top:6px}[dir='ltr'],[dir='rtl']{unicode-bidi:isolate;unicode-bidi:isolate}bdo[dir='ltr'],bdo[dir='rtl']{unicode-bidi:bidi-override;unicode-bidi:isolate-override;unicode-bidi:isolate-override}.GyAeWb{display:flex;justify-content:flex-start;flex-wrap:wrap;max-width:calc(var(--center-abs-margin)\\\n        \\ + var(--center-width) + var(--rhs-margin) + var(--rhs-width));}.srp{--center-abs-margin:180px;--center-width:652px;--rhs-margin:60px;--rhs-width:369px}@media\\\n        \\ (min-width:1459px) and (max-width:1659px){.srp{--center-abs-margin:calc(25vw\\\n        \\ + -184.75px)}}@media (min-width:1659px){.srp{--center-abs-margin:230px}}@media\\\n        \\ (min-width:1459px) and (max-width:1539px){.srp{--rhs-margin:calc(50vw +\\\n        \\ -669px)}}@media (min-width:1539px){.srp{--rhs-margin:100px}}@media (min-width:1121px)\\\n        \\ and (max-width:1300px){.srp{--center-abs-margin:calc((100vw - 1065px)/2)}}@media\\\n        \\ (max-width:1121px){.srp{--center-abs-margin:28px}}@media (max-width:1300px){.srp{--rhs-margin:44px}}.eqAnXb{font-size:medium;font-weight:normal;}.main{min-width:1121px;width:100%;}.s6JM6d{width:var(--center-width);position:relative;margin-left:var(--center-abs-margin);flex:0\\\n        \\ auto;}.e9EfHf{font-family:arial,sans-serif;clear:both;margin-left:0;padding-top:20px;box-sizing:border-box;position:relative;min-height:100vh;}.dodTBe{margin:6px\\\n        \\ 0 4px;height:65px;}.appbar{background:#fff;position:relative;-webkit-box-sizing:border-box;margin-left:var(--center-abs-margin)}</style></head><body\\\n        \\ jsmodel=\\\"hspDDf\\\" class=\\\"srp\\\" jscontroller=\\\"Eox39d\\\" marginheight=\\\"\\\n        3\\\" topmargin=\\\"3\\\" jsaction=\\\"rcuQ6b:npT2md\\\" id=\\\"gsr\\\"><style>.wYq63b{display:flex;left:0;position:absolute;top:0;z-index:1001}.S6VXfe{align-items:center;background-color:#fff;border-radius:0\\\n        \\ 2px 2px 0;box-shadow:0 2px 2px 0 rgba(0,0,0,.16),0 0 0 1px rgba(0,0,0,.08);display:flex;margin:80px\\\n        \\ auto 8px 0;overflow:hidden}.gyPpGe,.gyPpGe:visited,.qlVNAd{border:2px solid\\\n        \\ rgba(0,0,0,.16);border-radius:2px;color:#681da8;cursor:pointer;display:inline-block;font-size:14px;line-height:20px;margin:6px\\\n        \\ 11px;min-height:32px;text-decoration:underline;text-align:center;width:106px}.gyPpGe:not(:focus){clip:rect(1px,1px,1px,1px);overflow:hidden;position:absolute;padding:0}a.oBa0Fe{color:#70757a;float:right;font-style:italic;tap-highlight-color:rgba(0,0,0,0);tap-highlight-color:rgba(0,0,0,0)}a.aciXEb{padding:0\\\n        \\ 5px;}.CvDJxb{min-width:1121px;z-index:128;width:100%;position:absolute;top:20px;margin-top:6px;}.tsf{width:calc(var(--center-abs-margin)\\\n        \\ + 652px);}.Q3DXx{display:flex}.Q3DXx.yIbDgf{justify-content:space-between}.Q3DXx\\\n        \\ #gb,.Q3DXx #gb>div{float:none}.sfbg{background:#fff;height:69px;left:0;position:absolute;width:100%}.minidiv\\\n        \\ .sfbg{height:72px;overflow:hidden;background:#fff;box-shadow:0 1px 6px 0\\\n        \\ rgba(32, 33, 36, 0.28)}.A8SBwf,.IormK{width:692px;padding-left:27px}.A8SBwf{margin:0\\\n        \\ auto;margin-left:calc(var(--center-abs-margin) - 47px);position:relative;}.RNNXgb{display:flex;z-index:3;height:44px;background:#fff;border:1px\\\n        \\ solid transparent;box-shadow:0 2px 5px 1px rgba(64,60,67,.16);border-radius:24px;margin:0\\\n        \\ auto;width:690px;}.emcav .RNNXgb{border-bottom-left-radius:0;border-bottom-right-radius:0;box-shadow:0\\\n        \\ 2px 8px 1px rgba(64,60,67,.24);border-color:rgba(223,225,229,0)}.minidiv\\\n        \\ .emcav .RNNXgb{border-bottom-left-radius:0;border-bottom-right-radius:0;box-shadow:0\\\n        \\ 1px 6px rgba(32,33,36,.28);border-color:rgba(223,225,229,0);}.RNNXgb:hover,.sbfc\\\n        \\ .RNNXgb{background-color:#fff;box-shadow:0 2px 8px 1px rgba(64,60,67,.24);border-color:rgba(223,225,229,0)}.minidiv\\\n        \\ .RNNXgb:hover,.minidiv .sbfc .RNNXgb{border-color:rgba(223,225,229,0);box-shadow:0\\\n        \\ 1px 6px rgba(32,33,36,.28)}.SDkEP{flex:1;display:flex;padding:5px 4px 0\\\n        \\ 14px;}.logo{position:absolute;left:-139px;padding:4px 28px 0 30px;top:6px;}.iblpc\\\n        \\ span{display:none}.sbfc .iblpc span,.emcav .iblpc span{display:block}.iblpc{display:flex;align-items:center;padding-right:6px;margin-top:-7px}.sbfc\\\n        \\ .iblpc,.emcav .iblpc{padding-right:14px;margin-left:-1px}.sbfc.A8SBwf,.emcav.A8SBwf{padding-left:0;width:719px}.sbfc\\\n        \\ .RNNXgb,.emcav .RNNXgb{width:717px}@media (min-width:0){.emcav.A8SBwf.h3L8Ub{width:calc(var(--rhs-margin)\\\n        \\ + var(--rhs-width) + 699px)}.emcav.h3L8Ub .RNNXgb{width:calc(var(--rhs-margin)\\\n        \\ + var(--rhs-width) + 697px)}}@media (max-width:1300px){.emcav.A8SBwf.h3L8Ub{width:calc(var(--rhs-margin)\\\n        \\ + var(--rhs-width) + 547px)}.emcav.h3L8Ub .RNNXgb{width:calc(var(--rhs-margin)\\\n        \\ + var(--rhs-width) + 545px)}}.M8H8pb{position:absolute;top:0;left:0;right:0;padding:inherit;width:inherit}@media\\\n        \\ (max-width:1300px){.A8SBwf{margin-left:calc(var(--center-abs-margin) + 105px)}}@media\\\n        \\ (max-width:1300px){.A8SBwf,.IormK{width:540px}.RNNXgb{width:538px}.sbfc.A8SBwf,.emcav.A8SBwf{width:567px}.sbfc\\\n        \\ .RNNXgb,.emcav .RNNXgb{width:565px}}#logo{overflow:hidden;position:relative;display:block;}.jfN4p{border:0}.CcAdNb{margin:auto}.QCzoEc{color:#9aa0a6}.gLFyf,.YacQv{font:16px\\\n        \\ arial,sans-serif;line-height:34px;height:34px !important;font-size:16px;flex:100%;line-height:39px;height:39px\\\n        \\ !important;}.minidiv .gLFyf,.minidiv .YacQv{font-size:14px;line-height:32px;height:32px\\\n        \\ !important;}.gLFyf{background-color:transparent;border:none;margin:0;padding:0\\\n        \\ 0 3px;color:rgba(0,0,0,.87);word-wrap:break-word;outline:none;display:flex;tap-highlight-color:transparent;margin-top:-42px;}.a4bIc{display:flex;flex:1;flex-wrap:wrap}.YacQv{color:transparent;white-space:pre}.YacQv\\\n        \\ span{background:url(\\\"/images/experiments/wavy-underline.png\\\") repeat-x\\\n        \\ scroll 0 100% transparent;padding:0 0 7px 0}.gLFyf.i4ySpb{display:block}.Sxjlmb{white-space:nowrap;margin:20px;font-size:14px;font-weight:bold;line-height:normal;color:#fff}@keyframes\\\n        \\ g-snackbar-show {from{pointer-events:none;transform:translateY(0)}to{transform:translateY(-100%)}}@keyframes\\\n        \\ g-snackbar-hide {from{transform:translateY(-100%)}to{transform:translateY(0)}}@keyframes\\\n        \\ g-snackbar-show-content {from{opacity:0}}@keyframes g-snackbar-hide-content\\\n        \\ {to{opacity:0}}.tYAdEe,.SaJ9Qe{bottom:0;height:0;position:fixed;z-index:999}.FEXCIb,.CIKhFd{box-sizing:border-box;visibility:hidden}.EA3l1b{padding:0\\\n        \\ 24px}.Xb004{align-items:center;align-items:center;box-align:center;display:box;display:flex;display:flex}.lnctfd\\\n        \\ .Xb004{animation:g-snackbar-hide-content 350ms cubic-bezier(.4,0,.2,1) both;animation:g-snackbar-hide-content\\\n        \\ 350ms cubic-bezier(.4,0,.2,1) both}.ZWC4b .Xb004{animation:g-snackbar-show-content\\\n        \\ 350ms cubic-bezier(.4,0,.2,1) 150ms both;animation:g-snackbar-show-content\\\n        \\ 350ms cubic-bezier(.4,0,.2,1) 150ms both}.awHmMb.awHmMb{line-height:20px}.awHmMb{box-flex:1;flex:1\\\n        \\ 1 auto;margin:14px 0;word-break:break-word}@media (min-width:569px) and\\\n        \\ (min-height:569px){.tYAdEe,.SaJ9Qe{text-align:center}.CIKhFd,.FEXCIb{display:inline-block;max-width:568px;min-width:288px;text-align:left}.EA3l1b{border-radius:8px}.BDp8nf{margin-left:40px}}.SaJ9Qe{left:16px;right:auto}.qfY0Jf{font-weight:normal;border:1px\\\n        \\ solid #fff;border-radius:3px;padding:1px 3px 0 3px}.dRYYxd{display:flex;flex:0\\\n        \\ 0 auto;margin-top:-5px;align-items:stretch;flex-direction:row}.BKRPef{background:transparent;align-items:center;flex:1\\\n        \\ 0 auto;flex-direction:row;display:flex;cursor:pointer}.vOY7J{background:transparent;border:0;align-items:center;flex:1\\\n        \\ 0 auto;cursor:pointer;display:none;height:100%;line-height:44px;outline:none;padding:0\\\n        \\ 12px}.M2vV3{display:flex}.ExCKkf{height:100%;color:#70757a;vertical-align:middle;outline:none}.BKRPef{padding-right:4px}.ACRAdd{border-left:1px\\\n        \\ solid #dadce0;height:65%}.ACRAdd{display:none}.ACRAdd.M2vV3{display:block}.nDcEnd{flex:1\\\n        \\ 0 auto;display:flex;cursor:pointer;align-items:center;border:0;background:transparent;outline:none;padding:0\\\n        \\ 8px;width:24px;line-height:44px}.Gdd5U{height:24px;width:24px;vertical-align:middle}.Tg7LZd{height:44px;width:44px;background:transparent;border:none;cursor:pointer;flex:0\\\n        \\ 0 auto;padding:0;}.Tg7LZd{flex:0 0 auto;padding-right:13px}html:not(.zAoYTe)\\\n        \\ .Tg7LZd:focus{outline:none}.zgAlFc{background:none;color:#4285f4;height:24px;width:24px;margin:auto}.UUbT9{position:absolute;text-align:left;z-index:989;cursor:default;user-select:none;width:100%;margin-top:-1px;}.aajZCb{display:flex;flex-direction:column;list-style-type:none;margin:0;padding:0;overflow:hidden;background:#fff;border-radius:0\\\n        \\ 0 24px 24px;box-shadow:0 9px 8px -3px rgba(64,60,67,.24),8px 0 8px -7px\\\n        \\ rgba(64,60,67,.24),-8px 0 8px -7px rgba(64,60,67,.24);border:0;padding-bottom:4px;}.minidiv\\\n        \\ .aajZCb{box-shadow:0 4px 6px rgba(32,33,36,.28);border-bottom-left-radius:16px;border-bottom-right-radius:16px}.mkHrUc{display:flex;}.erkvQe{padding-bottom:16px;flex:auto;}.RjPuVb{height:1px;margin:0\\\n        \\ 26px 0 0;}.S3nFnd .RjPuVb,.S3nFnd .aajZCb{flex:0 0 auto}.xtSCL{border-top:1px\\\n        \\ solid #e8eaed;margin:0 14px;padding-bottom:4px}#shJ2Vb{display:none}.OBMEnb{padding:0;margin:0}.OBMEnb:not(:first-child){padding-top:8px}.G43f7e{display:flex;flex-direction:column;min-width:0;padding:0}#ynRric{display:none}.ynRric{list-style-type:none;flex-direction:column;color:#70757a;font-family:Google\\\n        \\ Sans,arial,sans-serif-medium,sans-serif;font-size:14px;margin:0 20px 0 16px;padding:8px\\\n        \\ 0 8px 0;line-height:16px;width:100%}.ynRric{letter-spacing:0;text-transform:none}.sbct{display:flex;align-items:center;min-width:0;max-height:none;padding:0;}.eIPGRd{flex:auto;display:flex;align-items:center;margin:0\\\n        \\ 20px 0 14px}.pcTkSc{display:flex;flex:auto;flex-direction:column;min-width:0;max-height:none;padding:6px\\\n        \\ 0}.sbic{display:flex;align-items:center;margin-right:14px;}.ClJ9Yb{line-height:12px;font-size:13px;color:#70757a;margin-top:2px}.wM6W7d{display:flex;font-size:16px;color:#212121;flex:auto;align-items:center;word-break:break-all;padding-right:8px}.wM6W7d\\\n        \\ span{flex:auto}.AQZ9Vd{display:flex;align-self:stretch;}#YMXe{display:none}@media\\\n        \\ (hover:hover){.sbai{visibility:hidden}.sbhl .sbai{visibility:inherit}}#TN4rFf{display:none}.IDVnvc{display:inline-block;max-width:223px;margin:8px\\\n        \\ -3px 8px 13px;border-radius:12px;height:178px;margin:-2px -10px 2px 10px;}.cRV9hb{width:90px;padding:6px;}.cRV9hb\\\n        \\ .pcTkSc{font-family:arial,sans-serif;overflow:hidden;margin-top:4px;padding:0;}.cRV9hb\\\n        \\ .pcTkSc .wM6W7d{font-size:14px;line-height:18px;padding:0;color:#202124}.cRV9hb\\\n        \\ .pcTkSc .ClJ9Yb{line-height:16px;font-size:12px;display:none;display:flex}.cRV9hb\\\n        \\ .pcTkSc .wM6W7d span,.cRV9hb .pcTkSc .ClJ9Yb span{overflow:hidden;text-overflow:ellipsis;-webkit-box-orient:vertical;display:-webkit-box;white-space:normal}.cRV9hb\\\n        \\ .pcTkSc .wM6W7d span{-webkit-line-clamp:2}.cRV9hb .pcTkSc .ClJ9Yb span{-webkit-line-clamp:2}.aVbWac{background:#fff;border-radius:12px;height:90px}@media\\\n        \\ (max-width:1300px){.A8SBwf:not(.h3L8Ub) .IDVnvc{height:167px}.A8SBwf:not(.h3L8Ub)\\\n        \\ .cRV9hb{width:79px}.A8SBwf:not(.h3L8Ub) .aVbWac{height:79px}.A8SBwf:not(.h3L8Ub)\\\n        \\ .aVbWac .sbic.vYOkbe{height:79px;width:79px}}.MG7lrf{font-size:8pt;margin-top:-16px;position:absolute;right:16px;}.c58wS{display:flex;margin-right:-14px;position:relative;z-index:99}</style><div\\\n        \\ id=\\\"_QyS5Y_SSL5HO5OUPp_mggAY_1\\\"></div><noscript><style>table,div,span,p{display:none}</style><meta\\\n        \\ content=\\\"0;url=/search?q=xkbcomp+alt+gr&amp;newwindow=1&amp;safe=off&amp;gbv=1&amp;sei=QyS5Y_SSL5HO5OUPp_mggAY\\\"\\\n        \\ http-equiv=\\\"refresh\\\"><div style=\\\"display:block\\\">Klik <a href=\\\"/search?q=xkbcomp+alt+gr&amp;newwindow=1&amp;safe=off&amp;gbv=1&amp;sei=QyS5Y_SSL5HO5OUPp_mggAY\\\"\\\n        >di sini</a> jika Anda tak dialihkan dalam beberapa detik.</div></noscript><style>@font-face{font-family:'Google\\\n        \\ Sans';font-style:normal;font-weight:400;font-display:optional;src:url(//fonts.gstatic.com/s/googlesans/v14/4UaGrENHsxJlGDuGo1OIlL3Kwp5MKg.woff2)format('woff2');unicode-range:U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116;}@font-face{font-family:'Google\\\n        \\ Sans';font-style:normal;font-weight:400;font-display:optional;src:url(//fonts.gstatic.com/s/googlesans/v14/4UaGrENHsxJlGDuGo1OIlL3Nwp5MKg.woff2)format('woff2');unicode-range:U+0370-03FF;}@font-face{font-family:'Google\\\n        \\ Sans';font-style:normal;font-weight:400;font-display:optional;src:url(//fonts.gstatic.com/s/googlesans/v14/4UaGrENHsxJlGDuGo1OIlL3Bwp5MKg.woff2)format('woff2');unicode-range:U+0102-0103,U+0110-0111,U+0128-0129,U+0168-0169,U+01A0-01A1,U+01AF-01B0,U+1EA0-1EF9,U+20AB;}@font-face{font-family:'Google\\\n        \\ Sans';font-style:normal;font-weight:400;font-display:optional;src:url(//fonts.gstatic.com/s/googlesans/v14/4UaGrENHsxJlGDuGo1OIlL3Awp5MKg.woff2)format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF;}@font-face{font-family:'Google\\\n        \\ Sans';font-style:normal;font-weight:400;font-display:optional;src:url(//fonts.gstatic.com/s/googlesans/v14/4UaGrENHsxJlGDuGo1OIlL3Owp4.woff2)format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD;}</style><script\\\n        \\ nonce=\\\"fiRsZQKlRm7AeAysGAgLGA\\\">(function(){var l='400';var font='Google\\\n        \\ Sans';(function(){if(document.fonts&&document.fonts.load)for(var d=l.split(\\\"\\\n        ,\\\"),b={},c=0,a=void 0;a=d[c];++c)b[a]||(b[a]=!0,document.fonts.load(a+\\\"\\\n        \\ 10pt \\\"+font).catch(function(){}))})();})();</script><h1 class=\\\"Uo8X3b\\\n        \\ OhScic zsYMMe\\\">Link Aksesibilitas</h1><div jscontroller=\\\"EufiNb\\\" class=\\\"\\\n        wYq63b\\\"><div class=\\\"S6VXfe\\\"><a jsname=\\\"BKxS1e\\\" class=\\\"gyPpGe\\\" role=\\\"\\\n        link\\\" tabindex=\\\"0\\\" jsaction=\\\"i3viod\\\" data-ved=\\\"0ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ67oDCAU\\\"\\\n        >Lewati ke konten utama</a><a jsname=\\\"KI37ad\\\" class=\\\"gyPpGe\\\" href=\\\"https://support.google.com/websearch/answer/181196?hl=id\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw1lemQM6qR3afjC9tVwr1UD\\\" data-ved=\\\"\\\n        0ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQwcMDCAY\\\">Bantuan aksesibilitas</a><div data-async-context=\\\"\\\n        async_id:duf3-78;authority:0;card_id:;entry_point:0;feature_id:;ftoe:0;header:0;is_jobs_spam_form:0;open:0;preselect_answer_index:-1;suggestions:;suggestions_subtypes:;suggestions_types:;surface:0;title:;type:78\\\"\\\n        ><div jscontroller=\\\"EkevXb\\\" style=\\\"display:none\\\" jsaction=\\\"rcuQ6b:npT2md\\\"\\\n        ></div><div id=\\\"duf3-78\\\" data-jiis=\\\"up\\\" data-async-type=\\\"duffy3\\\" data-async-context-required=\\\"\\\n        type,open,feature_id,async_id,entry_point,authority,card_id,ftoe,title,header,suggestions,surface,suggestions_types,suggestions_subtypes,preselect_answer_index,is_jobs_spam_form\\\"\\\n        \\ class=\\\"yp\\\" data-ved=\\\"0ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ-0EIBw\\\"></div><a\\\n        \\ jsname=\\\"JUypV\\\" class=\\\"gyPpGe\\\" data-async-trigger=\\\"duf3-78\\\" role=\\\"\\\n        link\\\" tabindex=\\\"0\\\" jsaction=\\\"trigger.szjOR\\\">Masukan aksesibilitas</a></div></div></div><div\\\n        \\ id=\\\"_QyS5Y_SSL5HO5OUPp_mggAY_3\\\"></div><div class=\\\"CvDJxb\\\" jscontroller=\\\"\\\n        tIj4fb\\\" jsaction=\\\"rcuQ6b:npT2md;\\\" id=\\\"searchform\\\"><div style=\\\"margin-top:-20px\\\"\\\n        \\ class=\\\"sfbg\\\"></div><div class=\\\"Q3DXx yIbDgf\\\"><form class=\\\"tsf\\\" action=\\\"\\\n        /search\\\" id=\\\"tsf\\\" data-submitfalse=\\\"q\\\" method=\\\"GET\\\" name=\\\"f\\\" role=\\\"\\\n        search\\\"> <div jsmodel=\\\"sj77Re P9Kqfe\\\" jsdata=\\\"MuIEvd;_;CQYHxk\\\"> <div\\\n        \\ jscontroller=\\\"cnjECf\\\" jsmodel=\\\"QubRsd a4L2gc QwwFZb BFDhle gx0hCb TnHSdd\\\n        \\ icv1ie \\\" class=\\\"A8SBwf\\\" data-adhe=\\\"false\\\" data-alt=\\\"true\\\" jsdata=\\\"\\\n        LVplcb;_;\\\" jsaction=\\\"lX6RWd:w3Wsmc;DkpM0b:d3sQLd;IQOavd:dFyQEf;XzZZPe:jI3wzf;Aghsf:AVsnlb;iHd9U:Q7Cnrc;f5hEHe:G0jgYd;vmxUb:j3bJnb;R2c5O:LuRugf;qiCkJd:ANdidc;htNNz:SNIJTd;NOg9L:HLgh3;uGoIkd:epUokb;zLdLw:eaGBS;rcuQ6b:npT2md\\\"\\\n        ><div class=\\\"logo\\\"><a href=\\\"https://www.google.ru/webhp?hl=id&amp;sa=X&amp;ved=0ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQPAgI\\\"\\\n        \\ title=\\\"Ke Beranda Google\\\" id=\\\"logo\\\" data-hveid=\\\"8\\\"><img class=\\\"jfN4p\\\"\\\n        \\ src=\\\"/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png\\\" style=\\\"\\\n        background:none\\\" alt=\\\"Google\\\" height=\\\"30\\\" width=\\\"92\\\"></a></div><div\\\n        \\ class=\\\"RNNXgb\\\" jsname=\\\"RNNXgb\\\"><div class=\\\"SDkEP\\\"><div class=\\\"iblpc\\\"\\\n        \\ jsname=\\\"uFMOof\\\"><div class=\\\"CcAdNb\\\"><span class=\\\"QCzoEc z1asCe MZy1Rb\\\"\\\n        \\ style=\\\"height:20px;line-height:20px;width:20px\\\"><svg focusable=\\\"false\\\"\\\n        \\ xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"0 0 24 24\\\"><path d=\\\"M15.5\\\n        \\ 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59\\\n        \\ 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01\\\n        \\ 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z\\\"></path></svg></span></div></div><div\\\n        \\ jscontroller=\\\"vZr2rb\\\" class=\\\"a4bIc\\\" jsname=\\\"gLFyf\\\" jsaction=\\\"h5M12e;input:d3sQLd;blur:jI3wzf\\\"\\\n        ><div class=\\\"YacQv\\\" jsname=\\\"vdLsw\\\"></div><div jsname=\\\"aJyGR\\\" jscontroller=\\\"\\\n        xMclgd\\\" class=\\\"gLFyf i4ySpb\\\" data-promo-open-duration=\\\"2000\\\" jsaction=\\\"\\\n        rcuQ6b:npT2md\\\"><g-snackbar jsname=\\\"nH91he\\\" jscontroller=\\\"OZLguc\\\" style=\\\"\\\n        display:none\\\" data-dismiss=\\\"\\\" jsshadow=\\\"\\\" jsaction=\\\"rcuQ6b:npT2md\\\"\\\n        ><div jsname=\\\"sM5MNb\\\" aria-live=\\\"polite\\\" class=\\\"SaJ9Qe\\\"><div jsname=\\\"\\\n        Ng57nc\\\" class=\\\"CIKhFd v0rrvd\\\" data-ved=\\\"0ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ4G8ICg\\\"\\\n        ><div class=\\\"EA3l1b\\\"><div class=\\\"Xb004\\\" jsslot=\\\"\\\"><span class=\\\"awHmMb\\\n        \\ wHYlTd yUTMj Sxjlmb\\\">Tekan <span class=\\\"qfY0Jf\\\">/</span> untuk langsung\\\n        \\ ke kotak penelusuran</span></div></div></div></div></g-snackbar></div><input\\\n        \\ class=\\\"gLFyf\\\" jsaction=\\\"paste:puy29d; mouseenter:MJEKMe; mouseleave:iFHZnf;\\\"\\\n        \\ maxlength=\\\"2048\\\" name=\\\"q\\\" type=\\\"text\\\" aria-autocomplete=\\\"both\\\" aria-haspopup=\\\"\\\n        false\\\" autocapitalize=\\\"off\\\" autocomplete=\\\"off\\\" autocorrect=\\\"off\\\" role=\\\"\\\n        combobox\\\" spellcheck=\\\"false\\\" value=\\\"xkbcomp alt gr\\\" aria-label=\\\"Cari\\\"\\\n        \\ data-ved=\\\"0ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ39UDCAs\\\"></div><div class=\\\"\\\n        dRYYxd\\\">   <div jscontroller=\\\"PymCCe\\\" jsname=\\\"RP0xob\\\" class=\\\"BKRPef\\\"\\\n        > <div class=\\\"M2vV3 vOY7J\\\" tabindex=\\\"0\\\" jsname=\\\"pkjasb\\\" aria-label=\\\"\\\n        Hapus\\\" role=\\\"button\\\" jsaction=\\\"AVsnlb;rcuQ6b:npT2md\\\" data-ved=\\\"0ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ05YFCAw\\\"\\\n        >  <span class=\\\"ExCKkf z1asCe rzyADb\\\" jsname=\\\"itVqKe\\\"><svg focusable=\\\"\\\n        false\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"0 0 24 24\\\"><path d=\\\"\\\n        M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41\\\n        \\ 17.59 19 19 17.59 13.41 12z\\\"></path></svg></span>   </div> <span jsname=\\\"\\\n        s1VaRe\\\" class=\\\"ACRAdd M2vV3\\\"></span> </div> <div jscontroller=\\\"lpsUAf\\\"\\\n        \\ jsname=\\\"R5mgy\\\" class=\\\"nDcEnd\\\" data-is-images-mode=\\\"false\\\" data-propagated-experiment-ids=\\\"\\\n        \\\" aria-label=\\\"Telusuri pakai gambar\\\" role=\\\"button\\\" tabindex=\\\"0\\\" jsaction=\\\"\\\n        rcuQ6b:npT2md;h5M12e\\\" data-ved=\\\"0ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQhqEICA0\\\"\\\n        ><img class=\\\"Gdd5U\\\" src=\\\"data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI0LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IlN0YW5kYXJkX3Byb2R1Y3RfaWNvbiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIKCSB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjE5MnB4IiBoZWlnaHQ9IjE5MnB4IiB2aWV3Qm94PSIwIDAgMTkyIDE5MiIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMTkyIDE5MiIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxyZWN0IGlkPSJib3VuZGluZ19ib3hfMV8iIGZpbGw9Im5vbmUiIHdpZHRoPSIxOTIiIGhlaWdodD0iMTkyIi8+CjxnIGlkPSJhcnRfbGF5ZXIiPgoJPGNpcmNsZSBpZD0iRG90IiBmaWxsPSIjNDI4NUY0IiBjeD0iOTYiIGN5PSIxMDQuMTUiIHI9IjI4Ii8+Cgk8cGF0aCBpZD0iUmVkIiBmaWxsPSIjRUE0MzM1IiBkPSJNMTYwLDcydjQwLjE1VjEzNmMwLDEuNjktMC4zNCwzLjI5LTAuODIsNC44MnYwdjBjLTEuNTcsNC45Mi01LjQzLDguNzgtMTAuMzUsMTAuMzVoMHYwCgkJYy0xLjUzLDAuNDktMy4xMywwLjgyLTQuODIsMC44Mkg2NmwxNiwxNmg1MGgxMmM0LjQyLDAsOC42My0wLjksMTIuNDYtMi41MWMzLjgzLTEuNjIsNy4yOC0zLjk2LDEwLjE3LTYuODYKCQljMS40NS0xLjQ1LDIuNzYtMy4wMywzLjkxLTQuNzRjMi4zLTMuNCwzLjk2LTcuMjgsNC44MS0xMS40NGMwLjQzLTIuMDgsMC42NS00LjI0LDAuNjUtNi40NXYtMTJWOTYuMTVWODRsLTYtMTlsLTEwLjgyLDIuMTgKCQlDMTU5LjY2LDY4LjcxLDE2MCw3MC4zMSwxNjAsNzJ6Ii8+Cgk8cGF0aCBpZD0iQmx1ZSIgZmlsbD0iIzQyODVGNCIgZD0iTTMyLDcyYzAtMS42OSwwLjM0LTMuMjksMC44Mi00LjgyYzEuNTctNC45Miw1LjQzLTguNzgsMTAuMzUtMTAuMzVDNDQuNzEsNTYuMzQsNDYuMzEsNTYsNDgsNTYKCQloOTZjMS42OSwwLDMuMjksMC4zNCw0LjgyLDAuODJjMCwwLDAsMCwwLDBMMTQ5LDQ1bC0xNy01bC0xNi0xNmgtMTMuNDRIOTZoLTYuNTZINzZMNjAsNDBINDhjLTE3LjY3LDAtMzIsMTQuMzMtMzIsMzJ2MTJ2MjBsMTYsMTYKCQlWNzJ6Ii8+Cgk8cGF0aCBpZD0iR3JlZW4iIGZpbGw9IiMzNEE4NTMiIGQ9Ik0xNDQsNDBoLTEybDE2LjgzLDE2LjgzYzEuMjMsMC4zOSwyLjM5LDAuOTMsMy40NywxLjU5YzIuMTYsMS4zMiwzLjk3LDMuMTMsNS4yOSw1LjI5CgkJYzAuNjYsMS4wOCwxLjIsMi4yNCwxLjU5LDMuNDd2MEwxNzYsODRWNzJDMTc2LDU0LjMzLDE2MS42Nyw0MCwxNDQsNDB6Ii8+Cgk8cGF0aCBpZD0iWWVsbG93IiBmaWxsPSIjRkJCQzA0IiBkPSJNNDgsMTY4aDM5Ljg5bC0xNi0xNkg0OGMtOC44MiwwLTE2LTcuMTgtMTYtMTZ2LTIzLjg5bC0xNi0xNlYxMzZDMTYsMTUzLjY3LDMwLjMzLDE2OCw0OCwxNjh6CgkJIi8+CjwvZz4KPC9zdmc+Cg==\\\"\\\n        \\ alt=\\\"Penelusuran kamera\\\"></div></div></div>  <button jsname=\\\"Tg7LZd\\\"\\\n        \\ class=\\\"Tg7LZd\\\" aria-label=\\\"Telusuri\\\" type=\\\"submit\\\" data-ved=\\\"0ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ4dUDCA4\\\"\\\n        > <div class=\\\"zgAlFc\\\"> <span class=\\\"z1asCe MZy1Rb\\\"><svg focusable=\\\"false\\\"\\\n        \\ xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"0 0 24 24\\\"><path d=\\\"M15.5\\\n        \\ 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59\\\n        \\ 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01\\\n        \\ 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z\\\"></path></svg></span> </div> </button>\\\n        \\ </div><div jscontroller=\\\"Dvn7fe\\\" class=\\\"UUbT9\\\" style=\\\"display:none\\\"\\\n        \\ jsname=\\\"UUbT9\\\" jsaction=\\\"mouseout:ItzDCd;mouseleave:MWfikb;hBEIVb:nUZ9le;YMFC3:VKssTb;vklu5c:k02QY;ldyIye:CmVOgc\\\"\\\n        ><div class=\\\"RjPuVb\\\" jsname=\\\"RjPuVb\\\"></div><div class=\\\"aajZCb\\\" jsname=\\\"\\\n        aajZCb\\\"><div class=\\\"xtSCL\\\"></div><div class=\\\"mkHrUc\\\"><div class=\\\"erkvQe\\\"\\\n        \\ jsname=\\\"erkvQe\\\" role=\\\"presentation\\\"></div><div class=\\\"rLrQHf\\\" jsname=\\\"\\\n        tovEib\\\" role=\\\"presentation\\\"></div></div><div jsname=\\\"E80e9e\\\" class=\\\"\\\n        OBMEnb\\\" id=\\\"shJ2Vb\\\" role=\\\"presentation\\\"><ul jsname=\\\"bw4e9b\\\" class=\\\"\\\n        G43f7e\\\" role=\\\"listbox\\\"></ul></div><div class=\\\"ynRric\\\" id=\\\"ynRric\\\" role=\\\"\\\n        presentation\\\"></div><li data-view-type=\\\"1\\\" class=\\\"sbct\\\" id=\\\"YMXe\\\" role=\\\"\\\n        presentation\\\"><div class=\\\"eIPGRd\\\"><div class=\\\"sbic\\\"></div><div class=\\\"\\\n        pcTkSc\\\" role=\\\"option\\\"><div class=\\\"wM6W7d\\\"><span></span></div><div class=\\\"\\\n        ClJ9Yb\\\"><span></span></div></div><div class=\\\"AQZ9Vd\\\"><div class=\\\"sbai\\\"\\\n        >Hapus</div></div></div></li><li class=\\\"IDVnvc\\\" data-view-type=\\\"6\\\" id=\\\"\\\n        TN4rFf\\\" role=\\\"presentation\\\"><div class=\\\"cRV9hb\\\"><div class=\\\"aVbWac\\\"\\\n        ><div class=\\\"sbic\\\"></div></div><div class=\\\"pcTkSc\\\" role=\\\"option\\\"><div\\\n        \\ class=\\\"wM6W7d\\\"><span></span></div><div class=\\\"ClJ9Yb\\\"><span></span></div></div></div></li></div><div\\\n        \\ jsname=\\\"JUypV\\\" jscontroller=\\\"OqGDve\\\" class=\\\"MG7lrf\\\" data-async-context=\\\"\\\n        async_id:duf3-46;authority:0;card_id:;entry_point:0;feature_id:;ftoe:0;header:0;is_jobs_spam_form:0;open:0;preselect_answer_index:-1;suggestions:;suggestions_subtypes:;suggestions_types:;surface:0;title:;type:46\\\"\\\n        ><div jscontroller=\\\"EkevXb\\\" style=\\\"display:none\\\" jsaction=\\\"rcuQ6b:npT2md\\\"\\\n        ></div><div id=\\\"duf3-46\\\" data-jiis=\\\"up\\\" data-async-type=\\\"duffy3\\\" data-async-context-required=\\\"\\\n        type,open,feature_id,async_id,entry_point,authority,card_id,ftoe,title,header,suggestions,surface,suggestions_types,suggestions_subtypes,preselect_answer_index,is_jobs_spam_form\\\"\\\n        \\ class=\\\"yp\\\" data-ved=\\\"0ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ-0EIDw\\\"></div><a\\\n        \\ class=\\\"oBa0Fe aciXEb\\\" href=\\\"#\\\" id=\\\"sbfblt\\\" data-async-trigger=\\\"duf3-46\\\"\\\n        \\ role=\\\"button\\\" jsaction=\\\"trigger.szjOR\\\" data-ved=\\\"0ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQtw8IEA\\\"\\\n        >Laporkan prediksi yang tidak pantas</a></div></div><div jsname=\\\"mvaK7d\\\"\\\n        \\ class=\\\"M8H8pb\\\" data-ved=\\\"0ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ4d8ICBE\\\"></div></div>\\\n        \\ <div style=\\\"background:url(/images/searchbox/desktop_searchbox_sprites318_hr.webp)\\\"\\\n        > </div> </div> <div id=\\\"tophf\\\"><input name=\\\"newwindow\\\" value=\\\"1\\\" type=\\\"\\\n        hidden\\\"><input name=\\\"safe\\\" value=\\\"off\\\" type=\\\"hidden\\\"><input value=\\\"\\\n        QyS5Y_SSL5HO5OUPp_mggAY\\\" name=\\\"ei\\\" type=\\\"hidden\\\"><input value=\\\"AK50M_UAAAAAY7kyU7nITlNWbBLZDJCo1Q0sfh7hJjP3\\\"\\\n        \\ disabled=\\\"true\\\" name=\\\"iflsig\\\" type=\\\"hidden\\\"></div></form><div class=\\\"\\\n        Q3DXx\\\"><div class=\\\"c58wS\\\"><div id=\\\"_QyS5Y_SSL5HO5OUPp_mggAY_5\\\" class=\\\"\\\n        IpDT1d\\\" style=\\\"z-index:1\\\"></div></div><div id=\\\"_QyS5Y_SSL5HO5OUPp_mggAY_7\\\"\\\n        ></div></div></div></div><div class=\\\"DH7hPe\\\"></div><div id=\\\"gac_scont\\\"\\\n        ></div><span class=\\\"kpshf line gsr bilit big mdm\\\" style=\\\"display:none\\\"\\\n        ></span><div class=\\\"main\\\" id=\\\"main\\\"><div jsmodel=\\\" ROaKxe\\\" class=\\\"\\\n        e9EfHf\\\" id=\\\"cnt\\\"><div class=\\\"dodTBe\\\" id=\\\"sfcnt\\\"></div><style>.NiU7Eb{height:36px;width:36px}@keyframes\\\n        \\ quantumWizBoxInkSpread{0%{transform:translate(-50%,-50%) scale(0.2)}to{transform:translate(-50%,-50%)\\\n        \\ scale(2.2)}}@keyframes quantumWizIconFocusPulse{0%{transform:translate(-50%,-50%)\\\n        \\ scale(1.5);opacity:0}to{transform:translate(-50%,-50%) scale(2);opacity:1}}@keyframes\\\n        \\ quantumWizRadialInkSpread{0%{transform:scale(1.5);opacity:0}to{transform:scale(2.5);opacity:1}}@keyframes\\\n        \\ quantumWizRadialInkFocusPulse{0%{transform:scale(2);opacity:0}to{transform:scale(2.5);opacity:1}}.O0WRkf{-moz-user-select:none;-moz-transition:background\\\n        \\ .2s .1s;transition:background .2s .1s;border:0;-moz-border-radius:3px;border-radius:3px;cursor:pointer;display:inline-block;font-size:14px;font-weight:500;min-width:4em;outline:none;overflow:hidden;position:relative;text-align:center;text-transform:uppercase;z-index:0}.A9jyad{font-size:13px;line-height:16px}.zZhnYe{-moz-transition:box-shadow\\\n        \\ .28s cubic-bezier(0.4,0,0.2,1);transition:box-shadow .28s cubic-bezier(0.4,0,0.2,1);background:#dfdfdf;-moz-box-shadow:0px\\\n        \\ 2px 2px 0px rgba(0,0,0,.14),0px 3px 1px -2px rgba(0,0,0,.12),0px 1px 5px\\\n        \\ 0px rgba(0,0,0,.2);box-shadow:0px 2px 2px 0px rgba(0,0,0,.14),0px 3px 1px\\\n        \\ -2px rgba(0,0,0,.12),0px 1px 5px 0px rgba(0,0,0,.2)}.zZhnYe.isActive{-moz-transition:box-shadow\\\n        \\ .28s cubic-bezier(0.4,0,0.2,1);transition:box-shadow .28s cubic-bezier(0.4,0,0.2,1);-moz-transition:background\\\n        \\ .8s;transition:background .8s;-moz-box-shadow:0px 8px 10px 1px rgba(0,0,0,.14),0px\\\n        \\ 3px 14px 2px rgba(0,0,0,.12),0px 5px 5px -3px rgba(0,0,0,.2);box-shadow:0px\\\n        \\ 8px 10px 1px rgba(0,0,0,.14),0px 3px 14px 2px rgba(0,0,0,.12),0px 5px 5px\\\n        \\ -3px rgba(0,0,0,.2)}.e3Duub,.e3Duub a,.e3Duub a:hover,.e3Duub a:link,.e3Duub\\\n        \\ a:visited{background:#4285f4;color:#fff}.HQ8yf,.HQ8yf a{color:#4285f4}.UxubU,.UxubU\\\n        \\ a{color:#fff}.ZFr60d{position:absolute;top:0;right:0;bottom:0;left:0;background-color:transparent}.O0WRkf.isFocused\\\n        \\ .ZFr60d{background-color:rgba(0,0,0,.12)}.UxubU.isFocused .ZFr60d{background-color:rgba(255,255,255,.3)}.e3Duub.isFocused\\\n        \\ .ZFr60d{background-color:rgba(0,0,0,0.122)}.HQ8yf.isFocused .ZFr60d{background-color:rgba(66,133,244,.15)}.Vwe4Vb{-moz-transform:translate(-50%,-50%)\\\n        \\ scale(0);transform:translate(-50%,-50%) scale(0);transition:opacity .2s\\\n        \\ ease,visibility 0s ease .2s,transform 0s ease .2s;transition:opacity .2s\\\n        \\ ease,visibility 0s ease .2s,transform 0s ease .2s,-webkit-transform 0s ease\\\n        \\ .2s;transition:opacity .2s ease,visibility 0s ease .2s,-webkit-transform\\\n        \\ 0s ease .2s;background-size:cover;left:0;opacity:0;pointer-events:none;position:absolute;top:0;visibility:hidden}.O0WRkf.isActive\\\n        \\ .Vwe4Vb{-moz-transform:translate(-50%,-50%) scale(2.2);transform:translate(-50%,-50%)\\\n        \\ scale(2.2);opacity:1;visibility:visible}.O0WRkf.isActive.isUndragged .Vwe4Vb{transition:-webkit-transform\\\n        \\ 0.3s cubic-bezier(0,0,0.2,1),opacity .2s cubic-bezier(0,0,0.2,1);transition:opacity\\\n        \\ .2s cubic-bezier(0,0,0.2,1),-webkit-transform 0.3s cubic-bezier(0,0,0.2,1);transition:transform\\\n        \\ 0.3s cubic-bezier(0,0,0.2,1),opacity .2s cubic-bezier(0,0,0.2,1);transition:transform\\\n        \\ 0.3s cubic-bezier(0,0,0.2,1),opacity .2s cubic-bezier(0,0,0.2,1),-webkit-transform\\\n        \\ 0.3s cubic-bezier(0,0,0.2,1)}.O0WRkf.isDeactivating .Vwe4Vb{-moz-transform:translate(-50%,-50%)\\\n        \\ scale(2.2);transform:translate(-50%,-50%) scale(2.2);visibility:visible}.oG5Srb\\\n        \\ .Vwe4Vb,.zZhnYe .Vwe4Vb{background-image:radial-gradient(circle farthest-side,rgba(0,0,0,.12),rgba(0,0,0,.12)\\\n        \\ 80%,rgba(0,0,0,0) 100%)}.HQ8yf .Vwe4Vb{background-image:radial-gradient(circle\\\n        \\ farthest-side,rgba(66,133,244,.25),rgba(66,133,244,.25) 80%,rgba(66,133,244,0)\\\n        \\ 100%)}.e3Duub .Vwe4Vb{background-image:radial-gradient(circle farthest-side,#3367d6,#3367d6\\\n        \\ 80%,rgba(51,103,214,0) 100%)}.UxubU .Vwe4Vb{background-image:radial-gradient(circle\\\n        \\ farthest-side,rgba(255,255,255,.3),rgba(255,255,255,.3) 80%,rgba(255,255,255,0)\\\n        \\ 100%)}.O0WRkf.isDisabled{-moz-box-shadow:none;box-shadow:none;color:rgba(68,68,68,0.502);cursor:default;fill:rgba(68,68,68,0.502)}.zZhnYe.isDisabled{background:rgba(153,153,153,.1)}.UxubU.isDisabled{color:rgba(255,255,255,0.502);fill:rgba(255,255,255,0.502)}.UxubU.zZhnYe.isDisabled{background:rgba(204,204,204,.1)}.CwaK9{position:relative}.RveJvd{display:inline-block;margin:.5em}.mUbCce{-moz-user-select:none;-moz-transition:background\\\n        \\ .3s;transition:background .3s;border:0;-moz-border-radius:50%;border-radius:50%;cursor:pointer;display:inline-block;flex-shrink:0;height:48px;outline:none;overflow:hidden;position:relative;text-align:center;width:48px;z-index:0}.mUbCce>.TpQm9d{height:48px;width:48px}.YYBxpf{-moz-border-radius:0;border-radius:0;overflow:visible}.fKz7Od{color:rgba(0,0,0,.54);fill:rgba(0,0,0,.54)}.p9Nwte{color:rgba(255,255,255,.75);fill:rgba(255,255,255,.75)}.fKz7Od.isFocused{background-color:rgba(0,0,0,.12)}.p9Nwte.isFocused{background-color:rgba(204,204,204,.25)}.YYBxpf.isFocused{background-color:transparent}.VTBa7b{-moz-transform:translate(-50%,-50%)\\\n        \\ scale(0);transform:translate(-50%,-50%) scale(0);transition:opacity .2s\\\n        \\ ease,visibility 0s ease .2s,transform 0s ease .2s;transition:opacity .2s\\\n        \\ ease,visibility 0s ease .2s,transform 0s ease .2s,-webkit-transform 0s ease\\\n        \\ .2s;transition:opacity .2s ease,visibility 0s ease .2s,-webkit-transform\\\n        \\ 0s ease .2s;background-size:cover;left:0;opacity:0;pointer-events:none;position:absolute;top:0;visibility:hidden}.YYBxpf.isFocused\\\n        \\ .VTBa7b{-moz-animation:quantumWizIconFocusPulse .7s infinite alternate;animation:quantumWizIconFocusPulse\\\n        \\ .7s infinite alternate;height:100%;left:50%;top:50%;width:100%;visibility:visible}.mUbCce.isActive\\\n        \\ .VTBa7b{-moz-transform:translate(-50%,-50%) scale(2.2);transform:translate(-50%,-50%)\\\n        \\ scale(2.2);opacity:1;visibility:visible}.mUbCce.isActive.isUndragged .VTBa7b{transition:-webkit-transform\\\n        \\ 0.3s cubic-bezier(0,0,0.2,1),opacity .2s cubic-bezier(0,0,0.2,1);transition:opacity\\\n        \\ .2s cubic-bezier(0,0,0.2,1),-webkit-transform 0.3s cubic-bezier(0,0,0.2,1);transition:transform\\\n        \\ 0.3s cubic-bezier(0,0,0.2,1),opacity .2s cubic-bezier(0,0,0.2,1);transition:transform\\\n        \\ 0.3s cubic-bezier(0,0,0.2,1),opacity .2s cubic-bezier(0,0,0.2,1),-webkit-transform\\\n        \\ 0.3s cubic-bezier(0,0,0.2,1)}.mUbCce.isDeactivating .VTBa7b{-moz-transform:translate(-50%,-50%)\\\n        \\ scale(2.2);transform:translate(-50%,-50%) scale(2.2);visibility:visible}.fKz7Od\\\n        \\ .VTBa7b{background-image:radial-gradient(circle farthest-side,rgba(0,0,0,.12),rgba(0,0,0,.12)\\\n        \\ 80%,rgba(0,0,0,0) 100%)}.p9Nwte .VTBa7b{background-image:radial-gradient(circle\\\n        \\ farthest-side,rgba(204,204,204,.25),rgba(204,204,204,.25) 80%,rgba(204,204,204,0)\\\n        \\ 100%)}.mUbCce.isDisabled{color:rgba(0,0,0,.26);fill:rgba(0,0,0,.26);cursor:default}.p9Nwte.isDisabled{color:rgba(255,255,255,0.502);fill:rgba(255,255,255,0.502)}.xjKiLb{position:relative;top:50%}.xjKiLb>span{display:inline-block;position:relative}.llhEMd{-moz-transition:opacity\\\n        \\ .15s cubic-bezier(0.4,0,0.2,1) .15s;transition:opacity .15s cubic-bezier(0.4,0,0.2,1)\\\n        \\ .15s;background-color:rgba(0,0,0,0.502);bottom:0;left:0;opacity:0;position:fixed;right:0;top:0;z-index:5000}.llhEMd.isOpen{-moz-transition:opacity\\\n        \\ .05s cubic-bezier(0.4,0,0.2,1);transition:opacity .05s cubic-bezier(0.4,0,0.2,1);opacity:1}.mjANdc{transition:-webkit-transform\\\n        \\ .4s cubic-bezier(0.4,0,0.2,1);transition:transform .4s cubic-bezier(0.4,0,0.2,1);transition:transform\\\n        \\ .4s cubic-bezier(0.4,0,0.2,1),-webkit-transform .4s cubic-bezier(0.4,0,0.2,1);-moz-box-align:center;box-align:center;align-items:center;display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex;-moz-box-orient:vertical;box-orient:vertical;flex-direction:column;bottom:0;left:0;padding:0\\\n        \\ 5%;position:absolute;right:0;top:0}.x3wWge,.ONJhl{display:block;height:3em}.eEPege>.x3wWge,.eEPege>.ONJhl{box-flex:1;flex-grow:1}.J9Nfi{flex-shrink:1;max-height:100%}.g3VIld{-moz-box-align:stretch;box-align:stretch;align-items:stretch;display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex;-moz-box-orient:vertical;box-orient:vertical;flex-direction:column;transition:-webkit-transform\\\n        \\ .225s cubic-bezier(0,0,0.2,1);transition:transform .225s cubic-bezier(0,0,0.2,1);transition:transform\\\n        \\ .225s cubic-bezier(0,0,0.2,1),-webkit-transform .225s cubic-bezier(0,0,0.2,1);position:relative;background-color:#fff;-moz-border-radius:2px;border-radius:2px;-moz-box-shadow:0\\\n        \\ 12px 15px 0 rgba(0,0,0,.24);box-shadow:0 12px 15px 0 rgba(0,0,0,.24);max-width:24em;outline:1px\\\n        \\ solid transparent;overflow:hidden}.vcug3d .g3VIld{padding:0}.g3VIld.kdCdqc{transition:-webkit-transform\\\n        \\ .15s cubic-bezier(0.4,0,1,1);transition:transform .15s cubic-bezier(0.4,0,1,1);transition:transform\\\n        \\ .15s cubic-bezier(0.4,0,1,1),-webkit-transform .15s cubic-bezier(0.4,0,1,1)}.Up8vH.CAwICe{transform:scale(0.8)}.Up8vH.kdCdqc{transform:scale(0.9)}.E4P6x.CAwICe,.E4P6x.kdCdqc{transform:translateY(50%)}.vDc8Ic.CAwICe{transform:scale(0.8)\\\n        \\ translateY(100%)}.XIJ9Ac>.x3wWge,.XIJ9Ac>.ONJhl,.HhoEBe>.x3wWge{box-flex:1;flex-grow:1}.HhoEBe>.ONJhl{box-flex:2;flex-grow:2}.Nevtdc>.x3wWge{box-flex:0;flex-grow:0}.Nevtdc>.ONJhl,.t8Vtv>.x3wWge{box-flex:1;flex-grow:1}.t8Vtv>.g3VIld{box-flex:2;flex-grow:2}.t8Vtv>.ONJhl{box-flex:1;flex-grow:1}.vcug3d{-moz-box-align:stretch;box-align:stretch;align-items:stretch;padding:0}.vcug3d>.g3VIld{box-flex:2;flex-grow:2;-moz-border-radius:0;border-radius:0;left:0;right:0;max-width:100%}.vcug3d>.ONJhl,.vcug3d>.x3wWge{box-flex:0;flex-grow:0;height:0}.tOrNgd{display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex;flex-shrink:0;font:500\\\n        \\ 20px Roboto,RobotoDraft,Helvetica,Arial,sans-serif;padding:24px 24px 20px\\\n        \\ 24px}.vcug3d .tOrNgd{display:none}.TNczib{box-pack:justify;justify-content:space-between;flex-shrink:0;-moz-box-shadow:0\\\n        \\ 3px 4px 0 rgba(0,0,0,.24);box-shadow:0 3px 4px 0 rgba(0,0,0,.24);background-color:#455a64;color:white;display:none;font:500\\\n        \\ 20px Roboto,RobotoDraft,Helvetica,Arial,sans-serif}.vcug3d .TNczib{display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex}.PNenzf{box-flex:1;flex-grow:1;flex-shrink:1;overflow:hidden;word-wrap:break-word}.TNczib\\\n        \\ .PNenzf{margin:16px 0}.VY7JQd{height:0}.TNczib .VY7JQd,.tOrNgd .bZWIgd{display:none}.R6Lfte\\\n        \\ .Wtw8H{flex-shrink:0;display:block;margin:-12px -6px 0 0}.PbnGhe{box-flex:2;flex-grow:2;flex-shrink:2;display:block;font:400\\\n        \\ 14px/20px Roboto,RobotoDraft,Helvetica,Arial,sans-serif;padding:0 24px;overflow-y:auto}.Whe8ub\\\n        \\ .PbnGhe{padding-top:24px}.hFEqNb .PbnGhe{padding-bottom:24px}.vcug3d .PbnGhe{padding:16px}.XfpsVe{display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex;flex-shrink:0;box-pack:end;justify-content:flex-end;padding:24px\\\n        \\ 24px 16px 24px}.vcug3d .XfpsVe{display:none}.OllbWe{box-pack:end;justify-content:flex-end;display:none}.vcug3d\\\n        \\ .OllbWe{display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex;-moz-box-align:start;box-align:start;align-items:flex-start;margin:0\\\n        \\ 16px}.kHssdc.O0WRkf.C0oVfc,.XfpsVe .O0WRkf.C0oVfc{min-width:64px}.kHssdc+.kHssdc{margin-left:8px}.TNczib\\\n        \\ .kHssdc{color:#fff;margin-top:10px}.TNczib .Wtw8H{margin:4px 24px 4px 0}.TNczib\\\n        \\ .kHssdc.isFocused,.TNczib .Wtw8H.isFocused{background-color:rgba(204,204,204,.25)}.TNczib\\\n        \\ .kHssdc>.Vwe4Vb,.TNczib .Wtw8H>.VTBa7b{background-image:radial-gradient(circle\\\n        \\ farthest-side,rgba(255,255,255,.3),rgba(255,255,255,.3) 80%,rgba(255,255,255,0)\\\n        \\ 100%)}.TNczib .kHssdc.isDisabled,.TNczib .Wtw8H.isDisabled{color:rgba(255,255,255,.5);fill:rgba(255,255,255,.5)}.YpNBdf{background-color:rgba(0,0,0,.502);bottom:0;left:0;position:fixed;right:0;top:0;z-index:5000}.kHMC4b{left:-moz-calc(50%\\\n        \\ - 14px);left:calc(50% - 14px);position:fixed;top:-moz-calc(50% - 14px);top:calc(50%\\\n        \\ - 14px)}.CBPqA .co39ub,.CBPqA .Cn087,.CBPqA .hfsr6b,.CBPqA .EjXFBf{border-color:#fff}.AEaMmf{background-color:#fff}.RZwAyb{overflow:visible}.zDZAFf{color:#70757a;font-size:12px;font-weight:normal;padding-top:8px}.f5p3jb{display:block;color:rgba(0,0,0,.87);cursor:pointer;font-size:14px;font-weight:normal;line-height:24px;padding:8px\\\n        \\ 0 8px 0;width:220px}.zPtOue{display:inline-block;vertical-align:bottom;margin-right:24px}.eJXHkf{height:24px;width:24px;vertical-align:middle}.bFDz0d{height:55px;visibility:hidden}.QLueyb{color:rgba(0,0,0,.54);font-size:13px;line-height:16px;padding-top:8px;padding-bottom:4px}.eL7Aze{cursor:pointer}.LARRVd{border-bottom:1px\\\n        \\ solid rgba(0,0,0,.12);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.c1AlVc{color:rgba(0,0,0,.87);font-size:16px;line-height:26px;overflow:hidden;text-decoration:none;text-overflow:ellipsis}.q3WZre{opacity:0}@media\\\n        \\ screen and (forced-colors:active){.R6Lfte .Wtw8H.fKz7Od{fill:ButtonText;color:ButtonText}}.EmVfjc{display:inline-block;position:relative;width:28px;height:28px}.Cg7hO{position:absolute;width:0;height:0;overflow:hidden}.xu46lf{width:100%;height:100%}.EmVfjc.isActive\\\n        \\ .xu46lf{animation:spinner-container-rotate 1568ms linear infinite}.ir3uv{position:absolute;width:100%;height:100%;opacity:0}.uWlRce{border-color:#4285f4}.GFoASc{border-color:#db4437}.WpeOqd{border-color:#f4b400}.rHV3jf{border-color:#0f9d58}.EmVfjc.isActive\\\n        \\ .ir3uv.uWlRce{animation:spinner-fill-unfill-rotate 5332ms cubic-bezier(0.4,0,0.2,1)\\\n        \\ infinite both,spinner-blue-fade-in-out 5332ms cubic-bezier(0.4,0,0.2,1)\\\n        \\ infinite both}.EmVfjc.isActive .ir3uv.GFoASc{animation:spinner-fill-unfill-rotate\\\n        \\ 5332ms cubic-bezier(0.4,0,0.2,1) infinite both,spinner-red-fade-in-out 5332ms\\\n        \\ cubic-bezier(0.4,0,0.2,1) infinite both}.EmVfjc.isActive .ir3uv.WpeOqd{animation:spinner-fill-unfill-rotate\\\n        \\ 5332ms cubic-bezier(0.4,0,0.2,1) infinite both,spinner-yellow-fade-in-out\\\n        \\ 5332ms cubic-bezier(0.4,0,0.2,1) infinite both}.EmVfjc.isActive .ir3uv.rHV3jf{animation:spinner-fill-unfill-rotate\\\n        \\ 5332ms cubic-bezier(0.4,0,0.2,1) infinite both,spinner-green-fade-in-out\\\n        \\ 5332ms cubic-bezier(0.4,0,0.2,1) infinite both}.HBnAAc{position:absolute;-moz-box-sizing:border-box;box-sizing:border-box;top:0;left:45%;width:10%;height:100%;overflow:hidden;border-color:inherit}.HBnAAc\\\n        \\ .X6jHbb{width:1000%;left:-450%}.xq3j6{display:inline-block;position:relative;width:50%;height:100%;overflow:hidden;border-color:inherit}.xq3j6\\\n        \\ .X6jHbb{width:200%}.X6jHbb{position:absolute;top:0;right:0;bottom:0;left:0;-moz-box-sizing:border-box;box-sizing:border-box;height:100%;border-width:3px;border-style:solid;border-color:inherit;border-bottom-color:transparent;-moz-border-radius:50%;border-radius:50%;animation:none}.xq3j6.ERcjC\\\n        \\ .X6jHbb{border-right-color:transparent;transform:rotate(129deg)}.xq3j6.dj3yTd\\\n        \\ .X6jHbb{left:-100%;border-left-color:transparent;transform:rotate(-129deg)}.EmVfjc.isActive\\\n        \\ .xq3j6.ERcjC .X6jHbb{animation:spinner-left-spin 1333ms cubic-bezier(0.4,0,0.2,1)\\\n        \\ infinite both}.EmVfjc.isActive .xq3j6.dj3yTd .X6jHbb{animation:spinner-right-spin\\\n        \\ 1333ms cubic-bezier(0.4,0,0.2,1) infinite both}.EmVfjc.sf4e6b .xu46lf{animation:spinner-container-rotate\\\n        \\ 1568ms linear infinite,spinner-fade-out 400ms cubic-bezier(0.4,0,0.2,1)}@keyframes\\\n        \\ spinner-container-rotate{to{transform:rotate(360deg)}}@keyframes spinner-fill-unfill-rotate{12.5%{transform:rotate(135deg)}25%{transform:rotate(270deg)}37.5%{transform:rotate(405deg)}50%{transform:rotate(540deg)}62.5%{transform:rotate(675deg)}75%{transform:rotate(810deg)}87.5%{transform:rotate(945deg)}to{transform:rotate(1080deg)}}@keyframes\\\n        \\ spinner-blue-fade-in-out{0%{opacity:.99}25%{opacity:.99}26%{opacity:0}89%{opacity:0}90%{opacity:.99}to{opacity:.99}}@keyframes\\\n        \\ spinner-red-fade-in-out{0%{opacity:0}15%{opacity:0}25%{opacity:.99}50%{opacity:.99}51%{opacity:0}}@keyframes\\\n        \\ spinner-yellow-fade-in-out{0%{opacity:0}40%{opacity:0}50%{opacity:.99}75%{opacity:.99}76%{opacity:0}}@keyframes\\\n        \\ spinner-green-fade-in-out{0%{opacity:0}65%{opacity:0}75%{opacity:.99}90%{opacity:.99}to{opacity:0}}@keyframes\\\n        \\ spinner-left-spin{0%{transform:rotate(130deg)}50%{transform:rotate(-5deg)}to{transform:rotate(130deg)}}@keyframes\\\n        \\ spinner-right-spin{0%{transform:rotate(-130deg)}50%{transform:rotate(5deg)}to{transform:rotate(-130deg)}}@keyframes\\\n        \\ spinner-fade-out{0%{opacity:.99}to{opacity:0}}.LARRVd{overflow-x:auto;text-overflow:unset;width:100%}.YrbPuc{color:#70757a;font-family:arial,sans-serif;font-size:14px;font-weight:400;line-height:22px}sentinel{}.awHmMb{color:#fff}.EA3l1b{background-color:#323232}sentinel{}.wHYlTd{font-family:arial,sans-serif;font-size:14px;line-height:22px}sentinel{}.yUTMj{font-family:arial,sans-serif;font-weight:400}sentinel{}.v0rrvd{padding-bottom:16px}sentinel{}.zJUuqf{margin-bottom:4px}sentinel{}.AB4Wff{margin-left:16px}sentinel{}.VDgVie{text-align:center}sentinel{}.TUOsUe{text-align:left}sentinel{}</style><script\\\n        \\ nonce=\\\"fiRsZQKlRm7AeAysGAgLGA\\\">(function(){google.tick(\\\"load\\\",\\\"sct\\\"\\\n        );}).call(this);</script>    <div data-st-cnt=\\\"ee\\\" id=\\\"easter-egg\\\"></div><div\\\n        \\ id=\\\"dc\\\"></div><style>.yg51vc{background:#fff;height:58px;padding:0;position:relative;z-index:126;white-space:nowrap;}.iJddsb{display:inline-block;fill:currentColor}.iJddsb\\\n        \\ img,.iJddsb svg{display:block;height:100%;width:100%}.rIbAWc{cursor:pointer;display:inline-block}.pdswFd{float:right;position:relative;right:17px;z-index:3}.pdswFd\\\n        \\ .hdtb-mitem{display:inline-block}.Lj8KXd{background-color:transparent;top:0;width:100%;white-space:nowrap;height:22px;position:absolute;transition:top\\\n        \\ 220ms ease-in-out;}.p4DDCd{display:none}.gTMtLb{z-index:1001;position:absolute;top:-1000px}.WE0UJf{height:43px}.LHJvCe{color:#70757a;display:flex;justify-content:space-between;transition:all\\\n        \\ 220ms ease-in-out;line-height:43px;min-width:652px;position:absolute;top:0}#result-stats{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:Google\\\n        \\ Sans,arial,sans-serif;padding-top:0px;padding-bottom:0;padding-right:8px;}@keyframes\\\n        \\ loading-pulse {from{opacity:0.2}to{opacity:1}}@keyframes ghost-card-shimmering\\\n        \\ {0%{transform:translateX(-100%)}100%{transform:translateX(100%)}}</style><div\\\n        \\ jscontroller=\\\"HYSCof\\\" class=\\\"gke0pe\\\" id=\\\"top_nav\\\" jsdata=\\\"Z1JpA;_;CQYHxo\\\"\\\n        \\ jsaction=\\\"rcuQ6b:npT2md\\\"><h1 class=\\\"Uo8X3b OhScic zsYMMe\\\">Mode Penelusuran</h1><div\\\n        \\ class=\\\"GLcBOb\\\" role=\\\"navigation\\\" id=\\\"hdtb\\\"><div class=\\\"yg51vc\\\" id=\\\"\\\n        pTwnEc\\\"><div class=\\\"IC1Ck\\\" id=\\\"hdtb-msb\\\"><div><div class=\\\"MUFPAc\\\"><div\\\n        \\ class=\\\"hdtb-mitem hdtb-msel\\\" aria-current=\\\"page\\\"><span><span class=\\\"\\\n        bmaJhd iJddsb\\\" style=\\\"height:16px;width:16px\\\"><svg focusable=\\\"false\\\"\\\n        \\ viewbox=\\\"0 0 24 24\\\"><path fill=\\\"#34a853\\\" d=\\\"M10 2v2a6 6 0 0 1 6 6h2a8\\\n        \\ 8 0 0 0-8-8\\\"></path><path fill=\\\"#ea4335\\\" d=\\\"M10 4V2a8 8 0 0 0-8 8h2c0-3.3\\\n        \\ 2.7-6 6-6\\\"></path><path fill=\\\"#fbbc04\\\" d=\\\"M4 10H2a8 8 0 0 0 8 8v-2c-3.3\\\n        \\ 0-6-2.69-6-6\\\"></path><path fill=\\\"#4285f4\\\" d=\\\"M22 20.59l-5.69-5.69A7.96\\\n        \\ 7.96 0 0 0 18 10h-2a6 6 0 0 1-6 6v2c1.85 0 3.52-.64 4.88-1.68l5.69 5.69L22\\\n        \\ 20.59\\\"></path></svg></span>Semua<div class=\\\"YTDezd\\\"></div></span></div><div\\\n        \\ class=\\\"hdtb-mitem\\\"><a href=\\\"/search?q=xkbcomp+alt+gr&amp;newwindow=1&amp;safe=off&amp;source=lnms&amp;tbm=shop&amp;sa=X&amp;ved=2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ_AUoAXoECAEQAw\\\"\\\n        \\ data-hveid=\\\"CAEQAw\\\"><span class=\\\"bmaJhd iJddsb\\\" style=\\\"height:16px;width:16px\\\"\\\n        ><svg focusable=\\\"false\\\" viewbox=\\\"0 0 24 24\\\"><path d=\\\"M21.11 2.89A3.02\\\n        \\ 3.02 0 0 0 18.95 2h-5.8c-.81 0-1.58.31-2.16.89L7.25 6.63 2.9 10.98a3.06\\\n        \\ 3.06 0 0 0 0 4.32l5.79 5.8a3.05 3.05 0 0 0 4.32.01l8.09-8.1c.58-.58.9-1.34.9-2.16v-5.8c0-.81-.32-1.59-.89-2.16zM20\\\n        \\ 10.85c0 .28-.12.54-.32.74l-3.73 3.74-4.36 4.36c-.41.41-1.08.41-1.49 0l-2.89-2.9-2.9-2.9a1.06\\\n        \\ 1.06 0 0 1 0-1.49l8.1-8.1c.2-.2.46-.3.74-.3l5.8-.01A1.05 1.05 0 0 1 20 5.05v5.8zM16\\\n        \\ 6c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2\\\"></path></svg></span>Shopping</a></div><div\\\n        \\ class=\\\"hdtb-mitem\\\"><a href=\\\"/search?q=xkbcomp+alt+gr&amp;newwindow=1&amp;safe=off&amp;source=lnms&amp;tbm=nws&amp;sa=X&amp;ved=2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ_AUoAnoECAEQBA\\\"\\\n        \\ data-hveid=\\\"CAEQBA\\\"><span class=\\\"bmaJhd iJddsb\\\" style=\\\"height:16px;width:16px\\\"\\\n        ><svg focusable=\\\"false\\\" viewbox=\\\"0 0 24 24\\\"><path d=\\\"M12 11h6v2h-6v-2zm-6\\\n        \\ 6h12v-2H6v2zm0-4h4V7H6v6zm16-7.22v12.44c0 1.54-1.34 2.78-3 2.78H5c-1.64\\\n        \\ 0-3-1.25-3-2.78V5.78C2 4.26 3.36 3 5 3h14c1.64 0 3 1.25 3 2.78zM19.99 12V5.78c0-.42-.46-.78-1-.78H5c-.54\\\n        \\ 0-1 .36-1 .78v12.44c0 .42.46.78 1 .78h14c.54 0 1-.36 1-.78V12zM12 9h6V7h-6v2\\\"\\\n        ></path></svg></span>Berita</a></div><div class=\\\"hdtb-mitem\\\"><a href=\\\"\\\n        /search?q=xkbcomp+alt+gr&amp;newwindow=1&amp;safe=off&amp;source=lnms&amp;tbm=vid&amp;sa=X&amp;ved=2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ_AUoA3oECAEQBQ\\\"\\\n        \\ data-hveid=\\\"CAEQBQ\\\"><span class=\\\"bmaJhd iJddsb\\\" style=\\\"height:16px;width:16px\\\"\\\n        ><svg focusable=\\\"false\\\" viewbox=\\\"0 0 24 24\\\"><path d=\\\"M10 16.5l6-4.5-6-4.5v9zM5\\\n        \\ 20h14a1 1 0 0 0 1-1V5a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1zm14.5\\\n        \\ 2H5a3 3 0 0 1-3-3V4.4A2.4 2.4 0 0 1 4.4 2h15.2A2.4 2.4 0 0 1 22 4.4v15.1a2.5\\\n        \\ 2.5 0 0 1-2.5 2.5\\\"></path></svg></span>Video</a></div><div class=\\\"hdtb-mitem\\\"\\\n        ><a href=\\\"/search?q=xkbcomp+alt+gr&amp;newwindow=1&amp;safe=off&amp;source=lnms&amp;tbm=isch&amp;sa=X&amp;ved=2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ_AUoBHoECAEQBg\\\"\\\n        \\ data-hveid=\\\"CAEQBg\\\"><span class=\\\"bmaJhd iJddsb\\\" style=\\\"height:16px;width:16px\\\"\\\n        ><svg focusable=\\\"false\\\" viewbox=\\\"0 0 24 24\\\"><path d=\\\"M14 13l4 5H6l4-4\\\n        \\ 1.79 1.78L14 13zm-6.01-2.99A2 2 0 0 0 8 6a2 2 0 0 0-.01 4.01zM22 5v14a3\\\n        \\ 3 0 0 1-3 2.99H5c-1.64 0-3-1.36-3-3V5c0-1.64 1.36-3 3-3h14c1.65 0 3 1.36\\\n        \\ 3 3zm-2.01 0a1 1 0 0 0-1-1H5a1 1 0 0 0-1 1v14a1 1 0 0 0 1 1h7v-.01h7a1 1\\\n        \\ 0 0 0 1-1V5\\\"></path></svg></span>Gambar</a></div></div><span class=\\\"hdtb-mitem\\\"\\\n        \\ jscontroller=\\\"nabPbb\\\" jsaction=\\\"KyPa0e:Y0y4c;BVfjhf:VFzweb;wjOG7e:gDkf4c;\\\"\\\n        ><g-popup jsname=\\\"V68bde\\\" jscontroller=\\\"DPreE\\\" jsaction=\\\"A05xBd:IYtByb;EOZ57e:WFrRFb;\\\"\\\n        \\ jsdata=\\\"mVjAjf;_;CQYHxs\\\"><div jsname=\\\"oYxtQd\\\" class=\\\"rIbAWc\\\" aria-expanded=\\\"\\\n        false\\\" aria-haspopup=\\\"true\\\" role=\\\"button\\\" tabindex=\\\"0\\\" jsaction=\\\"\\\n        WFrRFb;keydown:uYT2Vb\\\"><div jsname=\\\"LgbsSe\\\" class=\\\"GOE98c\\\"><span class=\\\"\\\n        MbEPDb z1asCe SaPW2b\\\" style=\\\"height:16px;line-height:16px;width:16px\\\"><svg\\\n        \\ focusable=\\\"false\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"0 0 24\\\n        \\ 24\\\"><path d=\\\"M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0 2c-1.1\\\n        \\ 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9\\\n        \\ 2-2-.9-2-2-2z\\\"></path></svg></span>Lainnya</div></div><div jsname=\\\"V68bde\\\"\\\n        \\ class=\\\"EwsJzb sAKBe B8Kd8d\\\" style=\\\"display:none;z-index:200\\\" id=\\\"_QyS5Y_SSL5HO5OUPp_mggAY_9\\\"\\\n        ></div></g-popup></span></div><div><div class=\\\"t2vtad\\\" id=\\\"hdtb-tls\\\" aria-controls=\\\"\\\n        hdtbMenus\\\" aria-expanded=\\\"false\\\" role=\\\"button\\\" tabindex=\\\"0\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ2x96BAgBEA4\\\">Alat</div></div></div><ol class=\\\"\\\n        pdswFd\\\" role=\\\"none\\\"></ol></div><div class=\\\"Lj8KXd p4DDCd\\\" id=\\\"hdtbMenus\\\"\\\n        \\ data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ3B96BAgBEA8\\\"><div id=\\\"tn_1\\\"\\\n        ></div></div></div></div><div id=\\\"before-appbar\\\"></div><div class=\\\"gTMtLb\\\n        \\ fp-nh\\\" id=\\\"lb\\\"></div><div class=\\\"appbar\\\" data-st-cnt=\\\"top\\\" id=\\\"\\\n        appbar\\\"><div id=\\\"extabar\\\"><div style=\\\"position:relative\\\"><div class=\\\"\\\n        WE0UJf\\\" id=\\\"slim_appbar\\\"><div class=\\\"LHJvCe\\\"><div id=\\\"result-stats\\\"\\\n        >Sekitar 15.600 hasil<nobr> (0,27 detik)&nbsp;</nobr></div></div></div></div></div><div\\\n        \\ jscontroller=\\\"sYEX8b\\\" jsname=\\\"GGAcbc\\\" class=\\\"AeB7Sc\\\" data-cssl=\\\"\\\n        /setprefs?hl=id&amp;prev=https://www.google.ru/search?newwindow%3D1%26safe%3Doff%26q%3Dxkbcomp%2Balt%2Bgr%26oq%3Dxkbcomp%2Balt%2Bgr%26gs_l%3Dserp.3..33i21.28976559.28977886.0.28978017.6.6.0.0.0.0.167.668.0j5.5.0....0...1c.1.64.serp..1.2.311.06cSKPTLo18%26pccc%3D1&amp;sig=0_fI0GmKthH8jgM-1KWGWa9pya5ps%3D&amp;cs=2\\\"\\\n        \\ id=\\\"spic_1\\\" role=\\\"dialog\\\" tabindex=\\\"-1\\\" jsaction=\\\"rcuQ6b:npT2md;Lhx8ef:hZ2GLc;UVNdjb;keydown:mivSOc\\\"\\\n        \\ data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQzNQJegQICxAB\\\"></div></div>\\\n        \\ <div id=\\\"_QyS5Y_SSL5HO5OUPp_mggAY_23\\\"></div> <div data-spl=\\\"/setprefs?hl=id&amp;prev=https://www.google.ru/search?newwindow%3D1%26safe%3Doff%26q%3Dxkbcomp%2Balt%2Bgr%26oq%3Dxkbcomp%2Balt%2Bgr%26gs_l%3Dserp.3..33i21.28976559.28977886.0.28978017.6.6.0.0.0.0.167.668.0j5.5.0....0...1c.1.64.serp..1.2.311.06cSKPTLo18%26pccc%3D1&amp;sig=0_fI0GmKthH8jgM-1KWGWa9pya5ps%3D&amp;cs=2\\\"\\\n        \\ id=\\\"YUIDDb\\\" style=\\\"display:none\\\"></div><div data-iatvcap=\\\"1\\\" id=\\\"\\\n        atvcap\\\"></div><div class=\\\"GyAeWb\\\" id=\\\"rcnt\\\"><div class=\\\"s6JM6d\\\" id=\\\"\\\n        center_col\\\"><style>.rsGxI.Ww4FFb,.Ww4FFb{background-color:#fff;border:0px;border-radius:0px;box-shadow:0px}.Ww4FFb\\\n        \\ .mnr-c,.mnr-c .Ww4FFb{box-shadow:none;margin-bottom:0px}.vt6azd{margin:0px\\\n        \\ 0px 30px}.BToiNc{display:flex;flex-direction:column;justify-content:flex-start}.kvH3mc{position:relative}.UK95Uc{contain:layout\\\n        \\ paint;overflow:hidden;}.Z26q7c{display:block;flex:0 0 auto}a:hover h3.LC20lb{text-decoration:underline}.M8OgIe\\\n        \\ .dG2XIf .fm06If .LC20lb,.n6SJS h3.LC20lb{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:100%}.LC20lb{display:inline-block;line-height:1.3;margin-bottom:3px;}.DKV0Md{padding-top:4px;padding-top:5px;}.VjDLd\\\n        \\ .TieM1d .tjvcx,.IVvPP .tjvcx,.kno-kp .tjvcx,.VjDLd .kp-wholepage-osrp .tjvcx,#rhs\\\n        \\ .ss6qqb .tjvcx,#rhs .trNcde .tjvcx{display:inline-block;height:19px;overflow-y:hidden}.qzEoUe{color:#202124;white-space:nowrap}.dyjrff{color:#5f6368}.B6fmyf{position:absolute;top:0;height:0;visibility:hidden;white-space:nowrap}.csDOgf{display:inline;visibility:visible;position:absolute}.eFM0qc{display:inline-flex;padding-bottom:2px;padding-top:1px;padding-left:2px;visibility:visible}.LAWljd{padding:0\\\n        \\ 4px 0 4px}.IjabWd{margin-left:2px}.xTFaxe{color:#70757a;top:2px}.D6lY4c{height:22px;width:22px;position:absolute;border-radius:11px;top:-1px}.iTPLzd{cursor:pointer;top:0;left:0;width:28px;z-index:1;line-height:16px;}.GUHazd{padding-bottom:12px}.eY4mx{padding-left:12px}.lUn2nc{padding-right:12px}.yXK7lf\\\n        \\ em{color:#5f6368}.yXK7lf a:visited em,.yXK7lf a em{color:inherit}.MUxGbd{padding-top:0px;margin-bottom:0px}.wuQ4Ob{color:#70757a}.WZ8Tjf{color:#70757a;}.lyLwlc{color:#202124}.yDYNvb.lyLwlc{color:#4d5156}.yDYNvb.lyLwlc\\\n        \\ b{color:#5f6368}.lEBKkf{display:-webkit-box;-webkit-box-orient:vertical;overflow:hidden}</style><div\\\n        \\ id=\\\"taw\\\"><div id=\\\"oFNiHe\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQL3oECCIQAg\\\"\\\n        ></div><div id=\\\"tvcap\\\"></div></div><div class=\\\"eqAnXb\\\" id=\\\"res\\\" role=\\\"\\\n        main\\\"><div id=\\\"topstuff\\\"></div><div id=\\\"search\\\"><div data-hveid=\\\"CCIQBA\\\"\\\n        \\ data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQGnoECCIQBA\\\"><h1 class=\\\"Uo8X3b\\\n        \\ OhScic zsYMMe\\\">Hasil Telusur</h1><div class=\\\"v7W49e\\\" eid=\\\"QyS5Y_SSL5HO5OUPp_mggAY\\\"\\\n        \\ data-async-context=\\\"query:xkbcomp%20alt%20gr\\\" id=\\\"rso\\\"><div class=\\\"\\\n        MjjYud\\\"><div jscontroller=\\\"SC7lYd\\\" class=\\\"g Ww4FFb vt6azd tF2Cxc\\\" lang=\\\"\\\n        en\\\" style=\\\"width:600px\\\" jsaction=\\\"QyLbLe:OMITjf;xd28Mb:A6j43c\\\" data-hveid=\\\"\\\n        CBoQAA\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFSgAegQIGhAA\\\"><div class=\\\"\\\n        kvH3mc BToiNc UK95Uc\\\" data-sokoban-container=\\\"ih6Jnb_E9w7xd\\\"><div class=\\\"\\\n        Z26q7c UK95Uc jGGQ5e\\\" data-header-feature=\\\"0\\\" data-sokoban-feature=\\\"x5WNvb\\\"\\\n        ><div class=\\\"yuRUbf\\\"><a href=\\\"https://unix.stackexchange.com/questions/204634/how-do-i-bind-altgrkey-to-a-symbol\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw0q9bhV8g02x_DCybiXElJc\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFnoECA4QAQ\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"\\\n        ><br><h3 class=\\\"LC20lb MBeuO DKV0Md\\\">How do I bind AltGr+key to a symbol?\\\n        \\ - Unix Stack Exchange</h3><div class=\\\"TbwUpd NJjxre\\\"><cite class=\\\"iUh30\\\n        \\ qLRx3b tjvcx\\\" role=\\\"text\\\">https://unix.stackexchange.com<span class=\\\"\\\n        dyjrff qzEoUe\\\" role=\\\"text\\\"> \\u203A ...</span></cite></div></a><div class=\\\"\\\n        B6fmyf\\\"><div class=\\\"TbwUpd\\\"><cite class=\\\"iUh30 qLRx3b tjvcx\\\" role=\\\"\\\n        text\\\">https://unix.stackexchange.com<span class=\\\"dyjrff qzEoUe\\\" role=\\\"\\\n        text\\\"> \\u203A ...</span></cite></div><div class=\\\"eFM0qc\\\"><span class=\\\"\\\n        LAWljd\\\"> \\xB7 </span><a class=\\\"fl iUh30\\\" href=\\\"https://translate.google.ru/translate?hl=id&amp;sl=en&amp;u=https://unix.stackexchange.com/questions/204634/how-do-i-bind-altgrkey-to-a-symbol&amp;prev=search&amp;pto=aue\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw297Upej3Xx00sbVfGHp6A2\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ7gF6BAgOEAU\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"\\\n        ><span>Terjemahkan halaman ini</span></a></div><div class=\\\"csDOgf\\\"><div\\\n        \\ jscontroller=\\\"exgaYe\\\" data-bsextraheight=\\\"0\\\" data-isdesktop=\\\"true\\\"\\\n        \\ jsdata=\\\"l7Bhpb;_;CQYHyw cECq7c;_;CQYHy4\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ2esEegQIDhAG\\\"\\\n        ><div jsaction=\\\"KyPa0e:RvIhPd;wjOG7e:edHC5b;al5F3e:edHC5b;\\\"><div role=\\\"\\\n        button\\\" tabindex=\\\"0\\\" jsaction=\\\"RvIhPd\\\" jsname=\\\"I3kE2c\\\" class=\\\"iTPLzd\\\n        \\ GUHazd lUn2nc eY4mx\\\" style=\\\"padding-bottom:20px;padding-right:5px;position:absolute\\\"\\\n        \\ aria-label=\\\"Tentang hasil ini\\\"><span jsname=\\\"czHhOd\\\" class=\\\"D6lY4c\\\"\\\n        ><span jsname=\\\"Bil8Ae\\\" class=\\\"xTFaxe IjabWd z1asCe SaPW2b\\\" style=\\\"height:18px;line-height:18px;width:18px\\\"\\\n        ><svg focusable=\\\"false\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"\\\n        0 0 24 24\\\"><path d=\\\"M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0\\\n        \\ 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2\\\n        \\ 2 2-.9 2-2-.9-2-2-2z\\\"></path></svg></span></span></div><span jsname=\\\"\\\n        zOVa8\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQh-4GegQIDhAH\\\"></span></div></div></div></div></div></div><div\\\n        \\ class=\\\"Z26q7c UK95Uc\\\" data-content-feature=\\\"1\\\" data-sokoban-feature=\\\"\\\n        nke7rc\\\"><div class=\\\"MUxGbd wuQ4Ob WZ8Tjf\\\"><span>20 Mei 2015</span><span\\\n        \\ aria-hidden=\\\"true\\\"> \\xB7 </span><span>3 jawaban</span></div><div class=\\\"\\\n        VwiC3b yXK7lf MUxGbd yDYNvb lyLwlc lEBKkf\\\" style=\\\"-webkit-line-clamp:2\\\"\\\n        >This is easy to do with xmodmap . You can use a keysym directive to change\\\n        \\ the keysyms associated with a key. This affect all the keys that\\_...</div></div><div\\\n        \\ class=\\\"Z26q7c UK95Uc\\\" data-content-feature=\\\"2\\\" data-sokoban-feature=\\\"\\\n        M7eMpf\\\"><div data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQqwJ6BAgJEAA\\\"><div\\\n        \\ class=\\\"P1usbc\\\"><div class=\\\"VNLkW\\\"><div class=\\\"i4vd5e\\\"><div class=\\\"\\\n        wrBvFf OSrXXb\\\"><a class=\\\"fl\\\" href=\\\"https://unix.stackexchange.com/questions/68178/remap-altgr-key-to-ac10-in-xkb\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw10KGSpQq1kgTHPVR32SpVi\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQrAIoAHoECAkQAQ\\\" target=\\\"_blank\\\" rel=\\\"\\\n        noopener\\\"><span>Remap <em>altgr</em> key to AC10 in <em>XKB</em> - Unix &amp;\\\n        \\ Linux ...</span></a></div></div><div class=\\\"G1Rrjc\\\"><div class=\\\"wrBvFf\\\n        \\ OSrXXb\\\"><span>1 jawaban</span></div></div><div class=\\\"G1Rrjc\\\"><div class=\\\"\\\n        wrBvFf OSrXXb\\\"><span>16 Mar 2013</span></div></div></div><div class=\\\"VNLkW\\\"\\\n        ><div class=\\\"i4vd5e\\\"><div class=\\\"wrBvFf OSrXXb\\\"><a class=\\\"fl\\\" href=\\\"\\\n        https://unix.stackexchange.com/questions/32606/is-there-a-way-to-remap-the-altgr-key-to-ctrl-with-setxkbmap\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw1vhb-t1moR5D3icTNi54q5\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQrAIoAXoECAkQAg\\\" target=\\\"_blank\\\" rel=\\\"\\\n        noopener\\\"><span>Is there a way to remap the <em>AltGr</em> key to Ctrl with\\\n        \\ ...</span></a></div></div><div class=\\\"G1Rrjc\\\"><div class=\\\"wrBvFf OSrXXb\\\"\\\n        ><span>5 jawaban</span></div></div><div class=\\\"G1Rrjc\\\"><div class=\\\"wrBvFf\\\n        \\ OSrXXb\\\"><span>25 Feb 2012</span></div></div></div><div class=\\\"VNLkW\\\"\\\n        ><div class=\\\"i4vd5e\\\"><div class=\\\"wrBvFf OSrXXb\\\"><a class=\\\"fl\\\" href=\\\"\\\n        https://unix.stackexchange.com/questions/312772/making-ctrl-alt-act-like-altgr-in-xkb\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw1VhjQWkBI-iIQx6cFox1kr\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQrAIoAnoECAkQAw\\\" target=\\\"_blank\\\" rel=\\\"\\\n        noopener\\\"><span>Making CTRL + ALT act like <em>AltGr</em> in <em>xkb</em>\\\n        \\ - Unix ...</span></a></div></div><div class=\\\"G1Rrjc\\\"><div class=\\\"wrBvFf\\\n        \\ OSrXXb\\\"><span>1 jawaban</span></div></div><div class=\\\"G1Rrjc\\\"><div class=\\\"\\\n        wrBvFf OSrXXb\\\"><span>27 Sep 2016</span></div></div></div><div class=\\\"VNLkW\\\"\\\n        ><div class=\\\"i4vd5e\\\"><div class=\\\"wrBvFf OSrXXb\\\"><a class=\\\"fl\\\" href=\\\"\\\n        https://unix.stackexchange.com/questions/79967/mapping-altgr-to-left-control\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw1YCpqUWDDkNEibXtuZ9N6g\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQrAIoA3oECAkQBA\\\" target=\\\"_blank\\\" rel=\\\"\\\n        noopener\\\"><span>Mapping <em>AltGr</em> to left control - Unix &amp; Linux\\\n        \\ Stack ...</span></a></div></div><div class=\\\"G1Rrjc\\\"><div class=\\\"wrBvFf\\\n        \\ OSrXXb\\\"><span>1 jawaban</span></div></div><div class=\\\"G1Rrjc\\\"><div class=\\\"\\\n        wrBvFf OSrXXb\\\"><span>20 Jun 2013</span></div></div></div><div class=\\\"k6DEPe\\\"\\\n        ><div class=\\\"i4vd5e\\\"><div class=\\\"wrBvFf OSrXXb\\\"><a class=\\\"fl\\\" href=\\\"\\\n        /search?q=xkbcomp+alt+gr+site:unix.stackexchange.com&amp;newwindow=1&amp;safe=off&amp;sa=X&amp;ved=2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQrQIoBHoECAkQBQ\\\"\\\n        ><span>Telusuran lainnya dari unix.stackexchange.com</span></a></div></div></div></div></div></div></div></div><span\\\n        \\ id=\\\"z9PoV\\\"></span><script nonce=\\\"fiRsZQKlRm7AeAysGAgLGA\\\">(function(){var\\\n        \\ uer=false;var eid='z9PoV';(function(){var a=uer,b=Date.now(),c=google.c.sxs?\\\"\\\n        load2\\\":\\\"load\\\";if(google.timers&&google.timers[c].t){var d=0;if(eid){var\\\n        \\ e=document.getElementById(eid);e&&(d=Math.floor(e.getBoundingClientRect().top+window.pageYOffset))}google.tick(c,\\\"\\\n        frt\\\",b);d&&google.c.e(c,\\\"frtp\\\",String(d));for(var f=d>=google.c.wh,g=document.getElementsByTagName(\\\"\\\n        img\\\"),h=0,k=void 0;k=g[h++];)google.c.setup(k,!0,d);google.c.frt=!1;f&&(a||google.c.ataf)&&google.c.ubr(!1,b,d,!a&&google.c.ataf)};}).call(this);})();</script></div><div\\\n        \\ class=\\\"MjjYud\\\"><div jscontroller=\\\"SC7lYd\\\" class=\\\"g Ww4FFb vt6azd tF2Cxc\\\"\\\n        \\ lang=\\\"en\\\" style=\\\"width:600px\\\" jsaction=\\\"QyLbLe:OMITjf;xd28Mb:A6j43c\\\"\\\n        \\ data-hveid=\\\"CBsQAA\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFSgAegQIGxAA\\\"\\\n        ><div class=\\\"kvH3mc BToiNc UK95Uc\\\" data-sokoban-container=\\\"ih6Jnb_DHlbYe\\\"\\\n        ><div class=\\\"Z26q7c UK95Uc jGGQ5e\\\" data-header-feature=\\\"0\\\" data-sokoban-feature=\\\"\\\n        x5WNvb\\\"><div class=\\\"yuRUbf\\\"><a href=\\\"https://askubuntu.com/questions/107626/remapping-the-altgr-key-to-control-with-setxkbmap\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw3WeeiEr0Q9E3Ytk_Kx56Ob\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFnoECA0QAQ\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"\\\n        ><br><h3 class=\\\"LC20lb MBeuO DKV0Md\\\">Remapping the AltGr key to Control\\\n        \\ with setxkbmap</h3><div class=\\\"TbwUpd NJjxre\\\"><cite class=\\\"iUh30 qLRx3b\\\n        \\ tjvcx\\\" role=\\\"text\\\">https://askubuntu.com<span class=\\\"dyjrff qzEoUe\\\"\\\n        \\ role=\\\"text\\\"> \\u203A rema...</span></cite></div></a><div class=\\\"B6fmyf\\\"\\\n        ><div class=\\\"TbwUpd\\\"><cite class=\\\"iUh30 qLRx3b tjvcx\\\" role=\\\"text\\\">https://askubuntu.com<span\\\n        \\ class=\\\"dyjrff qzEoUe\\\" role=\\\"text\\\"> \\u203A rema...</span></cite></div><div\\\n        \\ class=\\\"eFM0qc\\\"><span class=\\\"LAWljd\\\"> \\xB7 </span><a class=\\\"fl iUh30\\\"\\\n        \\ href=\\\"https://translate.google.ru/translate?hl=id&amp;sl=en&amp;u=https://askubuntu.com/questions/107626/remapping-the-altgr-key-to-control-with-setxkbmap&amp;prev=search&amp;pto=aue\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw2ziLt8bD_en-bxfuy8IcuE\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ7gF6BAgNEAU\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"\\\n        ><span>Terjemahkan halaman ini</span></a></div><div class=\\\"csDOgf\\\"><div\\\n        \\ jscontroller=\\\"exgaYe\\\" data-bsextraheight=\\\"0\\\" data-isdesktop=\\\"true\\\"\\\n        \\ jsdata=\\\"l7Bhpb;_;CQYHy0 cECq7c;_;CQYHy8\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ2esEegQIDRAG\\\"\\\n        ><div jsaction=\\\"KyPa0e:RvIhPd;wjOG7e:edHC5b;al5F3e:edHC5b;\\\"><div role=\\\"\\\n        button\\\" tabindex=\\\"0\\\" jsaction=\\\"RvIhPd\\\" jsname=\\\"I3kE2c\\\" class=\\\"iTPLzd\\\n        \\ GUHazd lUn2nc eY4mx\\\" style=\\\"padding-bottom:20px;padding-right:5px;position:absolute\\\"\\\n        \\ aria-label=\\\"Tentang hasil ini\\\"><span jsname=\\\"czHhOd\\\" class=\\\"D6lY4c\\\"\\\n        ><span jsname=\\\"Bil8Ae\\\" class=\\\"xTFaxe IjabWd z1asCe SaPW2b\\\" style=\\\"height:18px;line-height:18px;width:18px\\\"\\\n        ><svg focusable=\\\"false\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"\\\n        0 0 24 24\\\"><path d=\\\"M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0\\\n        \\ 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2\\\n        \\ 2 2-.9 2-2-.9-2-2-2z\\\"></path></svg></span></span></div><span jsname=\\\"\\\n        zOVa8\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQh-4GegQIDRAH\\\"></span></div></div></div></div></div></div><div\\\n        \\ class=\\\"Z26q7c UK95Uc\\\" data-content-feature=\\\"1\\\" data-sokoban-feature=\\\"\\\n        nke7rc\\\"><div class=\\\"MUxGbd wuQ4Ob WZ8Tjf\\\"><span>25 Feb 2012</span><span\\\n        \\ aria-hidden=\\\"true\\\"> \\xB7 </span><span>1 jawaban</span></div><div class=\\\"\\\n        VwiC3b yXK7lf MUxGbd yDYNvb lyLwlc lEBKkf\\\" style=\\\"-webkit-line-clamp:2\\\"\\\n        >Thanks to the suggestions of the Xorg community I found out the correct setxkbmap\\\n        \\ command: setxkbmap -option ctrl:ralt_rctrl.</div></div><div class=\\\"Z26q7c\\\n        \\ UK95Uc\\\" data-content-feature=\\\"2\\\" data-sokoban-feature=\\\"M7eMpf\\\"><div\\\n        \\ data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQqwJ6BAgKEAA\\\"><div class=\\\"\\\n        P1usbc\\\"><div class=\\\"VNLkW\\\"><div class=\\\"i4vd5e\\\"><div class=\\\"wrBvFf OSrXXb\\\"\\\n        ><a class=\\\"fl\\\" href=\\\"https://askubuntu.com/questions/1035761/typing-l-with-stroke-with-us-intl-altgr\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw03OedtccA5WQMPRiT-KLOM\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQrAIoAHoECAoQAQ\\\" target=\\\"_blank\\\" rel=\\\"\\\n        noopener\\\"><span>Typing L with stroke with us-intl-<em>altgr</em> - Ask Ubuntu</span></a></div></div><div\\\n        \\ class=\\\"G1Rrjc\\\"><div class=\\\"wrBvFf OSrXXb\\\"><span>2 jawaban</span></div></div><div\\\n        \\ class=\\\"G1Rrjc\\\"><div class=\\\"wrBvFf OSrXXb\\\"><span>13 Mei 2018</span></div></div></div><div\\\n        \\ class=\\\"VNLkW\\\"><div class=\\\"i4vd5e\\\"><div class=\\\"wrBvFf OSrXXb\\\"><a class=\\\"\\\n        fl\\\" href=\\\"https://askubuntu.com/questions/389869/how-to-type-%C3%B1-with-a-uk-keyboard-layout\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw2lMq9vpTIGEZ2DY3LbeqTF\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQrAIoAXoECAoQAg\\\" target=\\\"_blank\\\" rel=\\\"\\\n        noopener\\\"><span>How to type &quot;\\xF1&quot; with a UK keyboard layout? -\\\n        \\ Ask ...</span></a></div></div><div class=\\\"G1Rrjc\\\"><div class=\\\"wrBvFf\\\n        \\ OSrXXb\\\"><span>3 jawaban</span></div></div><div class=\\\"G1Rrjc\\\"><div class=\\\"\\\n        wrBvFf OSrXXb\\\"><span>12 Des 2013</span></div></div></div><div class=\\\"VNLkW\\\"\\\n        ><div class=\\\"i4vd5e\\\"><div class=\\\"wrBvFf OSrXXb\\\"><a class=\\\"fl\\\" href=\\\"\\\n        https://askubuntu.com/questions/29731/rebind-alt-key-to-win-using-setxkbmap\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw2rd9z93F3I9f0JTkq_QYjr\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQrAIoAnoECAoQAw\\\" target=\\\"_blank\\\" rel=\\\"\\\n        noopener\\\"><span>Rebind <em>Alt</em> key to win using setxkbmap? - Ask Ubuntu</span></a></div></div><div\\\n        \\ class=\\\"G1Rrjc\\\"><div class=\\\"wrBvFf OSrXXb\\\"><span>6 jawaban</span></div></div><div\\\n        \\ class=\\\"G1Rrjc\\\"><div class=\\\"wrBvFf OSrXXb\\\"><span>14 Des 2011</span></div></div></div><div\\\n        \\ class=\\\"VNLkW\\\"><div class=\\\"i4vd5e\\\"><div class=\\\"wrBvFf OSrXXb\\\"><a class=\\\"\\\n        fl\\\" href=\\\"https://askubuntu.com/questions/1084836/german-umlaut-with-windows-key-a-o-u-s\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw13drIjJZQt7hCtQee_0p-k\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQrAIoA3oECAoQBA\\\" target=\\\"_blank\\\" rel=\\\"\\\n        noopener\\\"><span>German umlaut with Windows key + a/o/u/s - Ask Ubuntu</span></a></div></div><div\\\n        \\ class=\\\"G1Rrjc\\\"><div class=\\\"wrBvFf OSrXXb\\\"><span>6 jawaban</span></div></div><div\\\n        \\ class=\\\"G1Rrjc\\\"><div class=\\\"wrBvFf OSrXXb\\\"><span>18 Okt 2018</span></div></div></div><div\\\n        \\ class=\\\"k6DEPe\\\"><div class=\\\"i4vd5e\\\"><div class=\\\"wrBvFf OSrXXb\\\"><a class=\\\"\\\n        fl\\\" href=\\\"/search?q=xkbcomp+alt+gr+site:askubuntu.com&amp;newwindow=1&amp;safe=off&amp;sa=X&amp;ved=2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQrQIoBHoECAoQBQ\\\"\\\n        ><span>Telusuran lainnya dari askubuntu.com</span></a></div></div></div></div></div></div></div></div></div><div\\\n        \\ class=\\\"MjjYud\\\"><div jscontroller=\\\"SC7lYd\\\" class=\\\"g Ww4FFb vt6azd tF2Cxc\\\"\\\n        \\ lang=\\\"en\\\" style=\\\"width:600px\\\" jsaction=\\\"QyLbLe:OMITjf;xd28Mb:A6j43c\\\"\\\n        \\ data-hveid=\\\"CBMQAA\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFSgAegQIExAA\\\"\\\n        ><div class=\\\"kvH3mc BToiNc UK95Uc\\\" data-sokoban-container=\\\"ih6Jnb_akzUhd\\\"\\\n        ><div class=\\\"Z26q7c UK95Uc jGGQ5e\\\" data-header-feature=\\\"0\\\" data-sokoban-feature=\\\"\\\n        x5WNvb\\\"><div class=\\\"yuRUbf\\\"><a href=\\\"https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=959071\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw0oVRYBGKJPlOFJrucpvv6Y\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFnoECAcQAQ\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"\\\n        ><br><h3 class=\\\"LC20lb MBeuO DKV0Md\\\">Xephyr and Xnest: AltGr combined keystrokes\\\n        \\ do not work ...</h3><div class=\\\"TbwUpd NJjxre\\\"><cite class=\\\"iUh30 qLRx3b\\\n        \\ tjvcx\\\" role=\\\"text\\\">https://bugs.debian.org<span class=\\\"dyjrff qzEoUe\\\"\\\n        \\ role=\\\"text\\\"> \\u203A cgi-bin</span></cite></div></a><div class=\\\"B6fmyf\\\"\\\n        ><div class=\\\"TbwUpd\\\"><cite class=\\\"iUh30 qLRx3b tjvcx\\\" role=\\\"text\\\">https://bugs.debian.org<span\\\n        \\ class=\\\"dyjrff qzEoUe\\\" role=\\\"text\\\"> \\u203A cgi-bin</span></cite></div><div\\\n        \\ class=\\\"eFM0qc\\\"><span class=\\\"LAWljd\\\"> \\xB7 </span><a class=\\\"fl iUh30\\\"\\\n        \\ href=\\\"https://translate.google.ru/translate?hl=id&amp;sl=en&amp;u=https://bugs.debian.org/cgi-bin/bugreport.cgi%3Fbug%3D959071&amp;prev=search&amp;pto=aue\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw1oIwZHkEzE4C-3mvwJN4nS\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ7gF6BAgHEAU\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"\\\n        ><span>Terjemahkan halaman ini</span></a></div><div class=\\\"csDOgf\\\"><div\\\n        \\ jscontroller=\\\"exgaYe\\\" data-bsextraheight=\\\"0\\\" data-isdesktop=\\\"true\\\"\\\n        \\ jsdata=\\\"l7Bhpb;_;CQYHyE cECq7c;_;CQYHyY\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ2esEegQIBxAG\\\"\\\n        ><div jsaction=\\\"KyPa0e:RvIhPd;wjOG7e:edHC5b;al5F3e:edHC5b;\\\"><div role=\\\"\\\n        button\\\" tabindex=\\\"0\\\" jsaction=\\\"RvIhPd\\\" jsname=\\\"I3kE2c\\\" class=\\\"iTPLzd\\\n        \\ GUHazd lUn2nc eY4mx\\\" style=\\\"padding-bottom:20px;padding-right:5px;position:absolute\\\"\\\n        \\ aria-label=\\\"Tentang hasil ini\\\"><span jsname=\\\"czHhOd\\\" class=\\\"D6lY4c\\\"\\\n        ><span jsname=\\\"Bil8Ae\\\" class=\\\"xTFaxe IjabWd z1asCe SaPW2b\\\" style=\\\"height:18px;line-height:18px;width:18px\\\"\\\n        ><svg focusable=\\\"false\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"\\\n        0 0 24 24\\\"><path d=\\\"M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0\\\n        \\ 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2\\\n        \\ 2 2-.9 2-2-.9-2-2-2z\\\"></path></svg></span></span></div><span jsname=\\\"\\\n        zOVa8\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQh-4GegQIBxAH\\\"></span></div></div></div></div></div></div><div\\\n        \\ class=\\\"Z26q7c UK95Uc\\\" data-content-feature=\\\"1\\\" data-sokoban-feature=\\\"\\\n        nke7rc\\\"><div class=\\\"VwiC3b yXK7lf MUxGbd yDYNvb lyLwlc lEBKkf\\\" style=\\\"\\\n        -webkit-line-clamp:2\\\"><span class=\\\"MUxGbd wuQ4Ob WZ8Tjf\\\"><span>29 Apr 2020</span>\\\n        \\ \\u2014 </span><span>Working <em>AltGr</em> (right Alt key) key on a German\\\n        \\ keyboard currently in Debian/testing Xnest and Xephyr report the following\\\n        \\ error,\\_...</span></div></div><div class=\\\"Z26q7c UK95Uc\\\" data-sokoban-feature=\\\"\\\n        bvRFlf\\\"></div></div></div></div><div class=\\\"MjjYud\\\"><div jscontroller=\\\"\\\n        SC7lYd\\\" class=\\\"g Ww4FFb vt6azd tF2Cxc\\\" lang=\\\"en\\\" style=\\\"width:600px\\\"\\\n        \\ jsaction=\\\"QyLbLe:OMITjf;xd28Mb:A6j43c\\\" data-hveid=\\\"CBIQAA\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFSgAegQIEhAA\\\"><div class=\\\"kvH3mc BToiNc\\\n        \\ UK95Uc\\\" data-sokoban-container=\\\"ih6Jnb_YtlLnd\\\"><div class=\\\"Z26q7c UK95Uc\\\n        \\ jGGQ5e\\\" data-header-feature=\\\"0\\\" data-sokoban-feature=\\\"x5WNvb\\\"><div\\\n        \\ class=\\\"yuRUbf\\\"><a href=\\\"https://bugzilla.redhat.com/show_bug.cgi?id=508434\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw2PyqIRDIUe-BipC8cTi6mB\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFnoECAYQAQ\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"\\\n        ><br><h3 class=\\\"LC20lb MBeuO DKV0Md\\\">508434 \\u2013 (altgr) iso-level-3 /\\\n        \\ alt-gr broken in rawhide</h3><div class=\\\"TbwUpd NJjxre\\\"><cite class=\\\"\\\n        iUh30 qLRx3b tjvcx\\\" role=\\\"text\\\">https://bugzilla.redhat.com<span class=\\\"\\\n        dyjrff qzEoUe\\\" role=\\\"text\\\"> \\u203A s...</span></cite></div></a><div class=\\\"\\\n        B6fmyf\\\"><div class=\\\"TbwUpd\\\"><cite class=\\\"iUh30 qLRx3b tjvcx\\\" role=\\\"\\\n        text\\\">https://bugzilla.redhat.com<span class=\\\"dyjrff qzEoUe\\\" role=\\\"text\\\"\\\n        > \\u203A s...</span></cite></div><div class=\\\"eFM0qc\\\"><span class=\\\"LAWljd\\\"\\\n        > \\xB7 </span><a class=\\\"fl iUh30\\\" href=\\\"https://translate.google.ru/translate?hl=id&amp;sl=en&amp;u=https://bugzilla.redhat.com/show_bug.cgi%3Fid%3D508434&amp;prev=search&amp;pto=aue\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw1WwR5Sq5E8H_j3Jmqcuadx\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ7gF6BAgGEAU\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"\\\n        ><span>Terjemahkan halaman ini</span></a></div><div class=\\\"csDOgf\\\"><div\\\n        \\ jscontroller=\\\"exgaYe\\\" data-bsextraheight=\\\"0\\\" data-isdesktop=\\\"true\\\"\\\n        \\ jsdata=\\\"l7Bhpb;_;CQYHyI cECq7c;_;CQYHyg\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ2esEegQIBhAG\\\"\\\n        ><div jsaction=\\\"KyPa0e:RvIhPd;wjOG7e:edHC5b;al5F3e:edHC5b;\\\"><div role=\\\"\\\n        button\\\" tabindex=\\\"0\\\" jsaction=\\\"RvIhPd\\\" jsname=\\\"I3kE2c\\\" class=\\\"iTPLzd\\\n        \\ GUHazd lUn2nc eY4mx\\\" style=\\\"padding-bottom:20px;padding-right:5px;position:absolute\\\"\\\n        \\ aria-label=\\\"Tentang hasil ini\\\"><span jsname=\\\"czHhOd\\\" class=\\\"D6lY4c\\\"\\\n        ><span jsname=\\\"Bil8Ae\\\" class=\\\"xTFaxe IjabWd z1asCe SaPW2b\\\" style=\\\"height:18px;line-height:18px;width:18px\\\"\\\n        ><svg focusable=\\\"false\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"\\\n        0 0 24 24\\\"><path d=\\\"M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0\\\n        \\ 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2\\\n        \\ 2 2-.9 2-2-.9-2-2-2z\\\"></path></svg></span></span></div><span jsname=\\\"\\\n        zOVa8\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQh-4GegQIBhAH\\\"></span></div></div></div></div></div></div><div\\\n        \\ class=\\\"Z26q7c UK95Uc\\\" data-content-feature=\\\"1\\\" data-sokoban-feature=\\\"\\\n        nke7rc\\\"><div class=\\\"VwiC3b yXK7lf MUxGbd yDYNvb lyLwlc lEBKkf\\\" style=\\\"\\\n        -webkit-line-clamp:2\\\"><span class=\\\"MUxGbd wuQ4Ob WZ8Tjf\\\"><span>11 Apr 2018</span>\\\n        \\ \\u2014 </span><span>Bug 508434 (<em>altgr</em>) - iso-level-3 / <em>alt</em>-<em>gr</em>\\\n        \\ broken in rawhide ... Output of '<em>xkbcomp</em> -<em>xkb</em> :0' (56.90\\\n        \\ KB, text/plain) 2009-07-10 12:00 UTC,\\_...</span></div></div><div class=\\\"\\\n        Z26q7c UK95Uc\\\" data-sokoban-feature=\\\"bvRFlf\\\"></div></div></div></div><div\\\n        \\ class=\\\"MjjYud\\\"><div jscontroller=\\\"SC7lYd\\\" class=\\\"g Ww4FFb vt6azd tF2Cxc\\\"\\\n        \\ lang=\\\"en\\\" style=\\\"width:600px\\\" jsaction=\\\"QyLbLe:OMITjf;xd28Mb:A6j43c\\\"\\\n        \\ data-hveid=\\\"CBQQAA\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFSgAegQIFBAA\\\"\\\n        ><div class=\\\"kvH3mc BToiNc UK95Uc\\\" data-sokoban-container=\\\"ih6Jnb_siiolc\\\"\\\n        ><div class=\\\"Z26q7c UK95Uc jGGQ5e\\\" data-header-feature=\\\"0\\\" data-sokoban-feature=\\\"\\\n        x5WNvb\\\"><div class=\\\"yuRUbf\\\"><a href=\\\"https://bbs.archlinux.org/viewtopic.php?id=62897\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw1STaGFGUA5f2saO6zDWhY3\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFnoECAwQAQ\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"\\\n        ><br><h3 class=\\\"LC20lb MBeuO DKV0Md\\\">alt gr rarely working under X [solved]\\\n        \\ / Applications &amp; Desktop ...</h3><div class=\\\"TbwUpd NJjxre\\\"><cite\\\n        \\ class=\\\"iUh30 qLRx3b tjvcx\\\" role=\\\"text\\\">https://bbs.archlinux.org<span\\\n        \\ class=\\\"dyjrff qzEoUe\\\" role=\\\"text\\\"> \\u203A vie...</span></cite></div></a><div\\\n        \\ class=\\\"B6fmyf\\\"><div class=\\\"TbwUpd\\\"><cite class=\\\"iUh30 qLRx3b tjvcx\\\"\\\n        \\ role=\\\"text\\\">https://bbs.archlinux.org<span class=\\\"dyjrff qzEoUe\\\" role=\\\"\\\n        text\\\"> \\u203A vie...</span></cite></div><div class=\\\"eFM0qc\\\"><span class=\\\"\\\n        LAWljd\\\"> \\xB7 </span><a class=\\\"fl iUh30\\\" href=\\\"https://translate.google.ru/translate?hl=id&amp;sl=en&amp;u=https://bbs.archlinux.org/viewtopic.php%3Fid%3D62897&amp;prev=search&amp;pto=aue\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw1X23lG6z8wFS_LqpgWpF7x\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ7gF6BAgMEAU\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"\\\n        ><span>Terjemahkan halaman ini</span></a></div><div class=\\\"csDOgf\\\"><div\\\n        \\ jscontroller=\\\"exgaYe\\\" data-bsextraheight=\\\"0\\\" data-isdesktop=\\\"true\\\"\\\n        \\ jsdata=\\\"l7Bhpb;_;CQYHyo cECq7c;_;CQYHys\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ2esEegQIDBAG\\\"\\\n        ><div jsaction=\\\"KyPa0e:RvIhPd;wjOG7e:edHC5b;al5F3e:edHC5b;\\\"><div role=\\\"\\\n        button\\\" tabindex=\\\"0\\\" jsaction=\\\"RvIhPd\\\" jsname=\\\"I3kE2c\\\" class=\\\"iTPLzd\\\n        \\ GUHazd lUn2nc eY4mx\\\" style=\\\"padding-bottom:20px;padding-right:5px;position:absolute\\\"\\\n        \\ aria-label=\\\"Tentang hasil ini\\\"><span jsname=\\\"czHhOd\\\" class=\\\"D6lY4c\\\"\\\n        ><span jsname=\\\"Bil8Ae\\\" class=\\\"xTFaxe IjabWd z1asCe SaPW2b\\\" style=\\\"height:18px;line-height:18px;width:18px\\\"\\\n        ><svg focusable=\\\"false\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"\\\n        0 0 24 24\\\"><path d=\\\"M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0\\\n        \\ 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2\\\n        \\ 2 2-.9 2-2-.9-2-2-2z\\\"></path></svg></span></span></div><span jsname=\\\"\\\n        zOVa8\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQh-4GegQIDBAH\\\"></span></div></div></div></div></div></div><div\\\n        \\ class=\\\"Z26q7c UK95Uc\\\" data-content-feature=\\\"1\\\" data-sokoban-feature=\\\"\\\n        nke7rc\\\"><div class=\\\"VwiC3b yXK7lf MUxGbd yDYNvb lyLwlc lEBKkf\\\" style=\\\"\\\n        -webkit-line-clamp:2\\\"><span class=\\\"MUxGbd wuQ4Ob WZ8Tjf\\\"><span>1 Jun 2010</span>\\\n        \\ \\u2014 </span><span>this problem occured a month ago, rarely at first, and\\\n        \\ now i often need to start X tens of times before <em>alt gr</em> works.\\\n        \\ i'm using x.org 1.4.2. xinit says that\\_...</span></div></div><div class=\\\"\\\n        Z26q7c UK95Uc\\\" data-content-feature=\\\"2\\\" data-sokoban-feature=\\\"M7eMpf\\\"\\\n        ><div data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQqwJ6BAgIEAA\\\"><div class=\\\"\\\n        P1usbc\\\"><div class=\\\"VNLkW\\\"><div class=\\\"i4vd5e\\\"><div class=\\\"wrBvFf OSrXXb\\\"\\\n        ><a class=\\\"fl\\\" href=\\\"https://bbs.archlinux.org/viewtopic.php?id=121216\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw1UqZs2qhfKcNiualhthvqp\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQrAIoAHoECAgQAQ\\\" target=\\\"_blank\\\" rel=\\\"\\\n        noopener\\\"><span>&quot;<em>Alt Gr</em>&quot; in X not working - Arch Linux\\\n        \\ Forums</span></a></div></div><div class=\\\"G1Rrjc\\\"><div class=\\\"wrBvFf OSrXXb\\\"\\\n        ><span>19 Jun 2011</span></div></div></div><div class=\\\"VNLkW\\\"><div class=\\\"\\\n        i4vd5e\\\"><div class=\\\"wrBvFf OSrXXb\\\"><a class=\\\"fl\\\" href=\\\"https://bbs.archlinux.org/viewtopic.php?id=29936\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw17Cf1dBBlPvHSweVQ5xwNO\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQrAIoAXoECAgQAg\\\" target=\\\"_blank\\\" rel=\\\"\\\n        noopener\\\"><span><em>Alt Gr</em> not working(google didn&#39;t help) / Applications\\\n        \\ &amp; Desktop ...</span></a></div></div><div class=\\\"G1Rrjc\\\"><div class=\\\"\\\n        wrBvFf OSrXXb\\\"><span>17 Jul 2010</span></div></div></div><div class=\\\"VNLkW\\\"\\\n        ><div class=\\\"i4vd5e\\\"><div class=\\\"wrBvFf OSrXXb\\\"><a class=\\\"fl\\\" href=\\\"\\\n        https://bbs.archlinux.org/viewtopic.php?id=68611\\\" data-jsarwt=\\\"1\\\" data-usg=\\\"\\\n        AOvVaw2Dd6Kjxgix0vpteZCdmbSz\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQrAIoAnoECAgQAw\\\"\\\n        \\ target=\\\"_blank\\\" rel=\\\"noopener\\\"><span>[SOLVED] <em>altgr</em> or others\\\n        \\ not working+us ... - Arch Linux Forums</span></a></div></div><div class=\\\"\\\n        G1Rrjc\\\"><div class=\\\"wrBvFf OSrXXb\\\"><span>29 Mar 2009</span></div></div></div><div\\\n        \\ class=\\\"VNLkW\\\"><div class=\\\"i4vd5e\\\"><div class=\\\"wrBvFf OSrXXb\\\"><a class=\\\"\\\n        fl\\\" href=\\\"https://bbs.archlinux.org/viewtopic.php?id=57913\\\" data-jsarwt=\\\"\\\n        1\\\" data-usg=\\\"AOvVaw2GTR7a_r4w4ktYo_oOFxbp\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQrAIoA3oECAgQBA\\\"\\\n        \\ target=\\\"_blank\\\" rel=\\\"noopener\\\"><span><em>AltGr</em> and <em>XKB</em>\\\n        \\ group switching - Arch Linux Forums</span></a></div></div><div class=\\\"\\\n        G1Rrjc\\\"><div class=\\\"wrBvFf OSrXXb\\\"><span>28 Okt 2008</span></div></div></div><div\\\n        \\ class=\\\"k6DEPe\\\"><div class=\\\"i4vd5e\\\"><div class=\\\"wrBvFf OSrXXb\\\"><a class=\\\"\\\n        fl\\\" href=\\\"/search?q=xkbcomp+alt+gr+site:bbs.archlinux.org&amp;newwindow=1&amp;safe=off&amp;sa=X&amp;ved=2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQrQIoBHoECAgQBQ\\\"\\\n        ><span>Telusuran lainnya dari bbs.archlinux.org</span></a></div></div></div></div></div></div></div></div></div><div\\\n        \\ class=\\\"MjjYud\\\"><div jscontroller=\\\"SC7lYd\\\" class=\\\"g Ww4FFb vt6azd tF2Cxc\\\"\\\n        \\ lang=\\\"en\\\" style=\\\"width:600px\\\" jsaction=\\\"QyLbLe:OMITjf;xd28Mb:A6j43c\\\"\\\n        \\ data-hveid=\\\"CBAQAA\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFSgAegQIEBAA\\\"\\\n        ><div class=\\\"kvH3mc BToiNc UK95Uc\\\" data-sokoban-container=\\\"ih6Jnb_ydhBZb\\\"\\\n        ><div class=\\\"Z26q7c UK95Uc jGGQ5e\\\" data-header-feature=\\\"0\\\" data-sokoban-feature=\\\"\\\n        x5WNvb\\\"><div class=\\\"yuRUbf\\\"><a href=\\\"https://github.com/termux/termux-x11/issues/37\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw0lCmQ6DqkOK92rnX7IIRYK\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFnoECAUQAQ\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"\\\n        ><br><h3 class=\\\"LC20lb MBeuO DKV0Md\\\">Set Keyboard Layout under XWayland\\\n        \\ #37 - GitHub</h3><div class=\\\"TbwUpd NJjxre\\\"><cite class=\\\"iUh30 qLRx3b\\\n        \\ tjvcx\\\" role=\\\"text\\\">https://github.com<span class=\\\"dyjrff qzEoUe\\\" role=\\\"\\\n        text\\\"> \\u203A issues</span></cite></div></a><div class=\\\"B6fmyf\\\"><div class=\\\"\\\n        TbwUpd\\\"><cite class=\\\"iUh30 qLRx3b tjvcx\\\" role=\\\"text\\\">https://github.com<span\\\n        \\ class=\\\"dyjrff qzEoUe\\\" role=\\\"text\\\"> \\u203A issues</span></cite></div><div\\\n        \\ class=\\\"eFM0qc\\\"><span class=\\\"LAWljd\\\"> \\xB7 </span><a class=\\\"fl iUh30\\\"\\\n        \\ href=\\\"https://translate.google.ru/translate?hl=id&amp;sl=en&amp;u=https://github.com/termux/termux-x11/issues/37&amp;prev=search&amp;pto=aue\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw2Kwg7XW8WkT7TXKevYC7sp\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ7gF6BAgFEAU\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"\\\n        ><span>Terjemahkan halaman ini</span></a></div><div class=\\\"csDOgf\\\"><div\\\n        \\ jscontroller=\\\"exgaYe\\\" data-bsextraheight=\\\"0\\\" data-isdesktop=\\\"true\\\"\\\n        \\ jsdata=\\\"l7Bhpb;_;CQYHyM cECq7c;_;CQYHyc\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ2esEegQIBRAG\\\"\\\n        ><div jsaction=\\\"KyPa0e:RvIhPd;wjOG7e:edHC5b;al5F3e:edHC5b;\\\"><div role=\\\"\\\n        button\\\" tabindex=\\\"0\\\" jsaction=\\\"RvIhPd\\\" jsname=\\\"I3kE2c\\\" class=\\\"iTPLzd\\\n        \\ GUHazd lUn2nc eY4mx\\\" style=\\\"padding-bottom:20px;padding-right:5px;position:absolute\\\"\\\n        \\ aria-label=\\\"Tentang hasil ini\\\"><span jsname=\\\"czHhOd\\\" class=\\\"D6lY4c\\\"\\\n        ><span jsname=\\\"Bil8Ae\\\" class=\\\"xTFaxe IjabWd z1asCe SaPW2b\\\" style=\\\"height:18px;line-height:18px;width:18px\\\"\\\n        ><svg focusable=\\\"false\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"\\\n        0 0 24 24\\\"><path d=\\\"M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0\\\n        \\ 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2\\\n        \\ 2 2-.9 2-2-.9-2-2-2z\\\"></path></svg></span></span></div><span jsname=\\\"\\\n        zOVa8\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQh-4GegQIBRAH\\\"></span></div></div></div></div></div></div><div\\\n        \\ class=\\\"Z26q7c UK95Uc\\\" data-content-feature=\\\"1\\\" data-sokoban-feature=\\\"\\\n        nke7rc\\\"><div class=\\\"VwiC3b yXK7lf MUxGbd yDYNvb lyLwlc lEBKkf\\\" style=\\\"\\\n        -webkit-line-clamp:2\\\"><span class=\\\"MUxGbd wuQ4Ob WZ8Tjf\\\"><span>25 Sep 2021</span>\\\n        \\ \\u2014 </span><span>Errors from <em>xkbcomp</em> are not fatal to the X\\\n        \\ server ... My Swiss french keyboard layout is working (with \\\"<em>Alt Gr</em>\\\"\\\n        \\ as \\\"Right Alt\\\" key).</span></div></div><div class=\\\"Z26q7c UK95Uc\\\" data-sokoban-feature=\\\"\\\n        bvRFlf\\\"></div></div></div></div><div class=\\\"MjjYud\\\"><div jscontroller=\\\"\\\n        SC7lYd\\\" class=\\\"g Ww4FFb vt6azd tF2Cxc\\\" lang=\\\"en\\\" style=\\\"width:600px\\\"\\\n        \\ jsaction=\\\"QyLbLe:OMITjf;xd28Mb:A6j43c\\\" data-hveid=\\\"CBEQAA\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFSgAegQIERAA\\\"><div class=\\\"kvH3mc BToiNc\\\n        \\ UK95Uc\\\" data-sokoban-container=\\\"ih6Jnb_ePzmPb\\\"><div class=\\\"Z26q7c UK95Uc\\\n        \\ jGGQ5e\\\" data-header-feature=\\\"0\\\" data-sokoban-feature=\\\"x5WNvb\\\"><div\\\n        \\ class=\\\"yuRUbf\\\"><a href=\\\"https://altgr-weur.eu/linux.html\\\" data-jsarwt=\\\"\\\n        1\\\" data-usg=\\\"AOvVaw1pKgfJ8zwiVlSoSDII4rpp\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFnoECAMQAQ\\\"\\\n        \\ target=\\\"_blank\\\" rel=\\\"noopener\\\"><br><h3 class=\\\"LC20lb MBeuO DKV0Md\\\"\\\n        >AltGr-WEur on Linux</h3><div class=\\\"TbwUpd NJjxre\\\"><cite class=\\\"iUh30\\\n        \\ qLRx3b tjvcx\\\" role=\\\"text\\\">https://altgr-weur.eu<span class=\\\"dyjrff qzEoUe\\\"\\\n        \\ role=\\\"text\\\"> \\u203A linux</span></cite></div></a><div class=\\\"B6fmyf\\\"\\\n        ><div class=\\\"TbwUpd\\\"><cite class=\\\"iUh30 qLRx3b tjvcx\\\" role=\\\"text\\\">https://altgr-weur.eu<span\\\n        \\ class=\\\"dyjrff qzEoUe\\\" role=\\\"text\\\"> \\u203A linux</span></cite></div><div\\\n        \\ class=\\\"eFM0qc\\\"><span class=\\\"LAWljd\\\"> \\xB7 </span><a class=\\\"fl iUh30\\\"\\\n        \\ href=\\\"https://translate.google.ru/translate?hl=id&amp;sl=en&amp;u=https://altgr-weur.eu/linux.html&amp;prev=search&amp;pto=aue\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw02jvcHfeqdiZksPqx7-naq\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ7gF6BAgDEAU\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"\\\n        ><span>Terjemahkan halaman ini</span></a></div><div class=\\\"csDOgf\\\"><div\\\n        \\ jscontroller=\\\"exgaYe\\\" data-bsextraheight=\\\"0\\\" data-isdesktop=\\\"true\\\"\\\n        \\ jsdata=\\\"l7Bhpb;_;CQYHyU cECq7c;_;CQYHyk\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ2esEegQIAxAG\\\"\\\n        ><div jsaction=\\\"KyPa0e:RvIhPd;wjOG7e:edHC5b;al5F3e:edHC5b;\\\"><div role=\\\"\\\n        button\\\" tabindex=\\\"0\\\" jsaction=\\\"RvIhPd\\\" jsname=\\\"I3kE2c\\\" class=\\\"iTPLzd\\\n        \\ GUHazd lUn2nc eY4mx\\\" style=\\\"padding-bottom:20px;padding-right:5px;position:absolute\\\"\\\n        \\ aria-label=\\\"Tentang hasil ini\\\"><span jsname=\\\"czHhOd\\\" class=\\\"D6lY4c\\\"\\\n        ><span jsname=\\\"Bil8Ae\\\" class=\\\"xTFaxe IjabWd z1asCe SaPW2b\\\" style=\\\"height:18px;line-height:18px;width:18px\\\"\\\n        ><svg focusable=\\\"false\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"\\\n        0 0 24 24\\\"><path d=\\\"M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0\\\n        \\ 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2\\\n        \\ 2 2-.9 2-2-.9-2-2-2z\\\"></path></svg></span></span></div><span jsname=\\\"\\\n        zOVa8\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQh-4GegQIAxAH\\\"></span></div></div></div></div></div></div><div\\\n        \\ class=\\\"Z26q7c UK95Uc\\\" data-content-feature=\\\"1\\\" data-sokoban-feature=\\\"\\\n        nke7rc\\\"><div class=\\\"VwiC3b yXK7lf MUxGbd yDYNvb lyLwlc lEBKkf\\\" style=\\\"\\\n        -webkit-line-clamp:2\\\"><span>https://<em>altgr</em>-weur.eu/map. Next, compile\\\n        \\ and activate the new map for the current display ($DISPLAY):. <em>xkbcomp</em>\\\n        \\ -w 0 -I$HOME/.config/<em>xkb</em>\\_...</span></div></div><div class=\\\"Z26q7c\\\n        \\ UK95Uc\\\" data-sokoban-feature=\\\"bvRFlf\\\"></div></div></div></div><div class=\\\"\\\n        MjjYud\\\"><div jscontroller=\\\"SC7lYd\\\" class=\\\"g Ww4FFb vt6azd tF2Cxc\\\" lang=\\\"\\\n        en\\\" style=\\\"width:600px\\\" jsaction=\\\"QyLbLe:OMITjf;xd28Mb:A6j43c\\\" data-hveid=\\\"\\\n        CA8QAA\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFSgAegQIDxAA\\\"><div class=\\\"\\\n        kvH3mc BToiNc UK95Uc\\\" data-sokoban-container=\\\"ih6Jnb_ZFmmNc\\\"><div class=\\\"\\\n        Z26q7c UK95Uc jGGQ5e\\\" data-header-feature=\\\"0\\\" data-sokoban-feature=\\\"x5WNvb\\\"\\\n        ><div class=\\\"yuRUbf\\\"><a href=\\\"https://forums.linuxmint.com/viewtopic.php?t=4162\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw0QzLTYlJTyWM2hba8H2mLH\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFnoECAQQAQ\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"\\\n        ><br><h3 class=\\\"LC20lb MBeuO DKV0Md\\\">Alt gr (right alt key) stopped working\\\n        \\ + XKB error - Linux Mint Forums</h3><div class=\\\"TbwUpd NJjxre\\\"><cite class=\\\"\\\n        iUh30 qLRx3b tjvcx\\\" role=\\\"text\\\">https://forums.linuxmint.com<span class=\\\"\\\n        dyjrff qzEoUe\\\" role=\\\"text\\\"> \\u203A ...</span></cite></div></a><div class=\\\"\\\n        B6fmyf\\\"><div class=\\\"TbwUpd\\\"><cite class=\\\"iUh30 qLRx3b tjvcx\\\" role=\\\"\\\n        text\\\">https://forums.linuxmint.com<span class=\\\"dyjrff qzEoUe\\\" role=\\\"text\\\"\\\n        > \\u203A ...</span></cite></div><div class=\\\"eFM0qc\\\"><span class=\\\"LAWljd\\\"\\\n        > \\xB7 </span><a class=\\\"fl iUh30\\\" href=\\\"https://translate.google.ru/translate?hl=id&amp;sl=en&amp;u=https://forums.linuxmint.com/viewtopic.php%3Ft%3D4162&amp;prev=search&amp;pto=aue\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw3wpAbeU6AC200FbV6vpkKh\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ7gF6BAgEEAU\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"\\\n        ><span>Terjemahkan halaman ini</span></a></div><div class=\\\"csDOgf\\\"><div\\\n        \\ jscontroller=\\\"exgaYe\\\" data-bsextraheight=\\\"0\\\" data-isdesktop=\\\"true\\\"\\\n        \\ jsdata=\\\"l7Bhpb;_;CQYHyA cECq7c;_;CQYHyQ\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ2esEegQIBBAG\\\"\\\n        ><div jsaction=\\\"KyPa0e:RvIhPd;wjOG7e:edHC5b;al5F3e:edHC5b;\\\"><div role=\\\"\\\n        button\\\" tabindex=\\\"0\\\" jsaction=\\\"RvIhPd\\\" jsname=\\\"I3kE2c\\\" class=\\\"iTPLzd\\\n        \\ GUHazd lUn2nc eY4mx\\\" style=\\\"padding-bottom:20px;padding-right:5px;position:absolute\\\"\\\n        \\ aria-label=\\\"Tentang hasil ini\\\"><span jsname=\\\"czHhOd\\\" class=\\\"D6lY4c\\\"\\\n        ><span jsname=\\\"Bil8Ae\\\" class=\\\"xTFaxe IjabWd z1asCe SaPW2b\\\" style=\\\"height:18px;line-height:18px;width:18px\\\"\\\n        ><svg focusable=\\\"false\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"\\\n        0 0 24 24\\\"><path d=\\\"M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0\\\n        \\ 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2\\\n        \\ 2 2-.9 2-2-.9-2-2-2z\\\"></path></svg></span></span></div><span jsname=\\\"\\\n        zOVa8\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQh-4GegQIBBAH\\\"></span></div></div></div></div></div></div><div\\\n        \\ class=\\\"Z26q7c UK95Uc\\\" data-content-feature=\\\"1\\\" data-sokoban-feature=\\\"\\\n        nke7rc\\\"><div class=\\\"VwiC3b yXK7lf MUxGbd yDYNvb lyLwlc lEBKkf\\\" style=\\\"\\\n        -webkit-line-clamp:2\\\"><span><em>Alt gr</em> (right alt key) stopped working\\\n        \\ + <em>XKB</em> error ... circumstanses: - an error in the library libxklavier\\\n        \\ - an error in th X-server (the tools <em>xkbcomp</em>,\\_...</span></div></div><div\\\n        \\ class=\\\"Z26q7c UK95Uc\\\" data-sokoban-feature=\\\"bvRFlf\\\"></div></div></div></div><div\\\n        \\ class=\\\"MjjYud\\\"><span class=\\\"oUAcPd\\\" id=\\\"fld_1\\\"></span><script nonce=\\\"\\\n        fiRsZQKlRm7AeAysGAgLGA\\\">(function(){var uer=false;var eid='fld_1';(function(){var\\\n        \\ a=uer,b=Date.now(),c=google.c.sxs?\\\"load2\\\":\\\"load\\\";if(google.timers&&google.timers[c].t){var\\\n        \\ d=0;if(eid){var e=document.getElementById(eid);e&&(d=Math.floor(e.getBoundingClientRect().top+window.pageYOffset))}for(var\\\n        \\ f=d>=google.c.wh,g=document.getElementsByTagName(\\\"img\\\"),h=0,k=void 0;k=g[h++];)google.c.setup(k,!1,d);f&&(a||google.c.ataf)&&google.c.ubr(!1,b,d,!a&&google.c.ataf)};}).call(this);})();</script><style>.hlcw0c{margin-bottom:44px}.AaVjTc\\\n        \\ a:link{display:block;color:#4285f4;font-weight:normal}.AaVjTc td{padding:0;text-align:center}.YyVfkd{color:#202124;font-weight:normal;}.AaVjTc{margin:30px\\\n        \\ auto 30px}.SJajHc{background:url(/images/nav_logo321.webp) no-repeat;background-size:167px;overflow:hidden;background-position:0\\\n        \\ 0;height:40px;display:block}.NVbCr{cursor:pointer}</style><div jscontroller=\\\"\\\n        SC7lYd\\\" class=\\\"g Ww4FFb vt6azd tF2Cxc\\\" lang=\\\"en\\\" style=\\\"width:600px\\\"\\\n        \\ jsaction=\\\"QyLbLe:OMITjf;xd28Mb:A6j43c\\\" data-hveid=\\\"CCUQAA\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFSgAegQIJRAA\\\"><div class=\\\"kvH3mc BToiNc\\\n        \\ UK95Uc\\\" data-sokoban-container=\\\"ih6Jnb_iOnZS\\\"><div class=\\\"Z26q7c UK95Uc\\\n        \\ jGGQ5e\\\" data-header-feature=\\\"0\\\" data-sokoban-feature=\\\"x5WNvb\\\"><div\\\n        \\ class=\\\"yuRUbf\\\"><a href=\\\"https://bugs.freedesktop.org/show_bug.cgi?id=19602\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw3xnwypGj3LPnSI6ISoBNX8\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFnoECCQQAQ\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"\\\n        ><br><h3 class=\\\"LC20lb MBeuO DKV0Md\\\">ALT-GR switch on german keyboard layout\\\n        \\ behaves weird</h3><div class=\\\"TbwUpd NJjxre\\\"><cite class=\\\"iUh30 qLRx3b\\\n        \\ tjvcx\\\" role=\\\"text\\\">https://bugs.freedesktop.org<span class=\\\"dyjrff qzEoUe\\\"\\\n        \\ role=\\\"text\\\"> \\u203A ...</span></cite></div></a><div class=\\\"B6fmyf\\\"><div\\\n        \\ class=\\\"TbwUpd\\\"><cite class=\\\"iUh30 qLRx3b tjvcx\\\" role=\\\"text\\\">https://bugs.freedesktop.org<span\\\n        \\ class=\\\"dyjrff qzEoUe\\\" role=\\\"text\\\"> \\u203A ...</span></cite></div><div\\\n        \\ class=\\\"eFM0qc\\\"><span class=\\\"LAWljd\\\"> \\xB7 </span><a class=\\\"fl iUh30\\\"\\\n        \\ href=\\\"https://translate.google.ru/translate?hl=id&amp;sl=en&amp;u=https://bugs.freedesktop.org/show_bug.cgi%3Fid%3D19602&amp;prev=search&amp;pto=aue\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw3cci450GCHNNHOFk08DOZR\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ7gF6BAgkEAU\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"\\\n        ><span>Terjemahkan halaman ini</span></a></div><div class=\\\"csDOgf\\\"><div\\\n        \\ jscontroller=\\\"exgaYe\\\" data-bsextraheight=\\\"0\\\" data-isdesktop=\\\"true\\\"\\\n        \\ jsdata=\\\"l7Bhpb;_;CQYHzE cECq7c;_;CQYHzQ\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ2esEegQIJBAG\\\"\\\n        ><div jsaction=\\\"KyPa0e:RvIhPd;wjOG7e:edHC5b;al5F3e:edHC5b;\\\"><div role=\\\"\\\n        button\\\" tabindex=\\\"0\\\" jsaction=\\\"RvIhPd\\\" jsname=\\\"I3kE2c\\\" class=\\\"iTPLzd\\\n        \\ GUHazd lUn2nc eY4mx\\\" style=\\\"padding-bottom:20px;padding-right:5px;position:absolute\\\"\\\n        \\ aria-label=\\\"Tentang hasil ini\\\"><span jsname=\\\"czHhOd\\\" class=\\\"D6lY4c\\\"\\\n        ><span jsname=\\\"Bil8Ae\\\" class=\\\"xTFaxe IjabWd z1asCe SaPW2b\\\" style=\\\"height:18px;line-height:18px;width:18px\\\"\\\n        ><svg focusable=\\\"false\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"\\\n        0 0 24 24\\\"><path d=\\\"M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0\\\n        \\ 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2\\\n        \\ 2 2-.9 2-2-.9-2-2-2z\\\"></path></svg></span></span></div><span jsname=\\\"\\\n        zOVa8\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQh-4GegQIJBAH\\\"></span></div></div></div></div></div></div><div\\\n        \\ class=\\\"Z26q7c UK95Uc\\\" data-content-feature=\\\"1\\\" data-sokoban-feature=\\\"\\\n        nke7rc\\\"><div class=\\\"VwiC3b yXK7lf MUxGbd yDYNvb lyLwlc lEBKkf\\\" style=\\\"\\\n        -webkit-line-clamp:2\\\"><span class=\\\"MUxGbd wuQ4Ob WZ8Tjf\\\"><span>9 Jul 2009</span>\\\n        \\ \\u2014 </span><span>Actually, yes. Please attach the output of \\\"<em>xkbcomp</em>\\\n        \\ -<em>xkb</em> :0 -\\\" after and before running setxkbmap (i.e. once when\\\n        \\ it works, once when it doesn't)\\_...</span></div></div><div class=\\\"Z26q7c\\\n        \\ UK95Uc\\\" data-sokoban-feature=\\\"bvRFlf\\\"></div></div></div></div><div class=\\\"\\\n        hlcw0c\\\"><div class=\\\"MjjYud\\\"><div jscontroller=\\\"SC7lYd\\\" class=\\\"g Ww4FFb\\\n        \\ vt6azd tF2Cxc\\\" lang=\\\"en\\\" style=\\\"width:600px\\\" jsaction=\\\"QyLbLe:OMITjf;xd28Mb:A6j43c\\\"\\\n        \\ data-hveid=\\\"CCYQAA\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFSgAegQIJhAA\\\"\\\n        ><div class=\\\"kvH3mc BToiNc UK95Uc\\\" data-sokoban-container=\\\"ih6Jnb_KOKHSe\\\"\\\n        ><div class=\\\"Z26q7c UK95Uc jGGQ5e\\\" data-header-feature=\\\"0\\\" data-sokoban-feature=\\\"\\\n        x5WNvb\\\"><div class=\\\"yuRUbf\\\"><a href=\\\"https://forums.gentoo.org/viewtopic-t-808068-start-0.html\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw2XNCy0gS9Kr1G9H3rjX5EH\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQFnoECCMQAQ\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"\\\n        ><br><h3 class=\\\"LC20lb MBeuO DKV0Md\\\">KDE: wrong key mappings with right\\\n        \\ alt / alt-gr [solved]</h3><div class=\\\"TbwUpd NJjxre\\\"><cite class=\\\"iUh30\\\n        \\ qLRx3b tjvcx\\\" role=\\\"text\\\">https://forums.gentoo.org<span class=\\\"dyjrff\\\n        \\ qzEoUe\\\" role=\\\"text\\\"> \\u203A vi...</span></cite></div></a><div class=\\\"\\\n        B6fmyf\\\"><div class=\\\"TbwUpd\\\"><cite class=\\\"iUh30 qLRx3b tjvcx\\\" role=\\\"\\\n        text\\\">https://forums.gentoo.org<span class=\\\"dyjrff qzEoUe\\\" role=\\\"text\\\"\\\n        > \\u203A vi...</span></cite></div><div class=\\\"eFM0qc\\\"><span class=\\\"LAWljd\\\"\\\n        > \\xB7 </span><a class=\\\"fl iUh30\\\" href=\\\"https://translate.google.ru/translate?hl=id&amp;sl=en&amp;u=https://forums.gentoo.org/viewtopic-t-808068-start-0.html&amp;prev=search&amp;pto=aue\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw01rhoMmkpPeYzEOU9kxuJ4\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ7gF6BAgjEAU\\\" target=\\\"_blank\\\" rel=\\\"noopener\\\"\\\n        ><span>Terjemahkan halaman ini</span></a></div><div class=\\\"csDOgf\\\"><div\\\n        \\ jscontroller=\\\"exgaYe\\\" data-bsextraheight=\\\"0\\\" data-isdesktop=\\\"true\\\"\\\n        \\ jsdata=\\\"l7Bhpb;_;CQYHzI cECq7c;_;CQYHzM\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ2esEegQIIxAG\\\"\\\n        ><div jsaction=\\\"KyPa0e:RvIhPd;wjOG7e:edHC5b;al5F3e:edHC5b;\\\"><div role=\\\"\\\n        button\\\" tabindex=\\\"0\\\" jsaction=\\\"RvIhPd\\\" jsname=\\\"I3kE2c\\\" class=\\\"iTPLzd\\\n        \\ GUHazd lUn2nc eY4mx\\\" style=\\\"padding-bottom:20px;padding-right:5px;position:absolute\\\"\\\n        \\ aria-label=\\\"Tentang hasil ini\\\"><span jsname=\\\"czHhOd\\\" class=\\\"D6lY4c\\\"\\\n        ><span jsname=\\\"Bil8Ae\\\" class=\\\"xTFaxe IjabWd z1asCe SaPW2b\\\" style=\\\"height:18px;line-height:18px;width:18px\\\"\\\n        ><svg focusable=\\\"false\\\" xmlns=\\\"http://www.w3.org/2000/svg\\\" viewBox=\\\"\\\n        0 0 24 24\\\"><path d=\\\"M12 8c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm0\\\n        \\ 2c-1.1 0-2 .9-2 2s.9 2 2 2 2-.9 2-2-.9-2-2-2zm0 6c-1.1 0-2 .9-2 2s.9 2 2\\\n        \\ 2 2-.9 2-2-.9-2-2-2z\\\"></path></svg></span></span></div><span jsname=\\\"\\\n        zOVa8\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQh-4GegQIIxAH\\\"></span></div></div></div></div></div></div><div\\\n        \\ class=\\\"Z26q7c UK95Uc\\\" data-content-feature=\\\"1\\\" data-sokoban-feature=\\\"\\\n        nke7rc\\\"><div class=\\\"MUxGbd wuQ4Ob WZ8Tjf\\\"><span>22 Des 2009</span><span\\\n        \\ aria-hidden=\\\"true\\\"> \\xB7 </span><span>6 postingan</span><span aria-hidden=\\\"\\\n        true\\\"> \\xB7 </span><span>2 penulis</span></div><div class=\\\"VwiC3b yXK7lf\\\n        \\ MUxGbd yDYNvb lyLwlc lEBKkf\\\" style=\\\"-webkit-line-clamp:2\\\"><span>Errors\\\n        \\ from <em>xkbcomp</em> are not fatal to the X server (EE) Logitech Logitech\\\n        \\ Illuminated Keyboard: failed to initialize for relative axes.</span></div></div><div\\\n        \\ class=\\\"Z26q7c UK95Uc\\\" data-sokoban-feature=\\\"bvRFlf\\\"></div></div></div></div></div></div></div></div></div><div\\\n        \\ id=\\\"bottomads\\\"></div><div id=\\\"botstuff\\\"><div data-hveid=\\\"CB0QAA\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQCHoECB0QAA\\\"><div id=\\\"bres\\\"></div><div\\\n        \\ role=\\\"navigation\\\"><h1 class=\\\"Uo8X3b OhScic zsYMMe\\\">Navigasi Halaman</h1><table\\\n        \\ class=\\\"AaVjTc\\\" style=\\\"border-collapse:collapse;text-align:left\\\" role=\\\"\\\n        presentation\\\"><tr jsname=\\\"TeSSVd\\\" valign=\\\"top\\\"><td class=\\\"d6cvqb BBwThe\\\"\\\n        ><span class=\\\"SJajHc\\\" style=\\\"background:url(/images/nav_logo321.webp) no-repeat;background-position:-24px\\\n        \\ 0;background-size:167px;width:28px\\\"></span></td><td class=\\\"YyVfkd\\\"><span\\\n        \\ class=\\\"SJajHc\\\" style=\\\"background:url(/images/nav_logo321.webp) no-repeat;background-position:-53px\\\n        \\ 0;background-size:167px;width:20px\\\"></span>1</td><td><a aria-label=\\\"Page\\\n        \\ 2\\\" class=\\\"fl\\\" href=\\\"/search?q=xkbcomp+alt+gr&amp;newwindow=1&amp;safe=off&amp;ei=QyS5Y_SSL5HO5OUPp_mggAY&amp;start=10&amp;sa=N&amp;ved=2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ8tMDegQIHRAE\\\"\\\n        ><span class=\\\"SJajHc NVbCr\\\" style=\\\"background:url(/images/nav_logo321.webp)\\\n        \\ no-repeat;background-position:-74px 0;background-size:167px;width:20px\\\"\\\n        ></span>2</a></td><td><a aria-label=\\\"Page 3\\\" class=\\\"fl\\\" href=\\\"/search?q=xkbcomp+alt+gr&amp;newwindow=1&amp;safe=off&amp;ei=QyS5Y_SSL5HO5OUPp_mggAY&amp;start=20&amp;sa=N&amp;ved=2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ8tMDegQIHRAG\\\"\\\n        ><span class=\\\"SJajHc NVbCr\\\" style=\\\"background:url(/images/nav_logo321.webp)\\\n        \\ no-repeat;background-position:-74px 0;background-size:167px;width:20px\\\"\\\n        ></span>3</a></td><td><a aria-label=\\\"Page 4\\\" class=\\\"fl\\\" href=\\\"/search?q=xkbcomp+alt+gr&amp;newwindow=1&amp;safe=off&amp;ei=QyS5Y_SSL5HO5OUPp_mggAY&amp;start=30&amp;sa=N&amp;ved=2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ8tMDegQIHRAI\\\"\\\n        ><span class=\\\"SJajHc NVbCr\\\" style=\\\"background:url(/images/nav_logo321.webp)\\\n        \\ no-repeat;background-position:-74px 0;background-size:167px;width:20px\\\"\\\n        ></span>4</a></td><td><a aria-label=\\\"Page 5\\\" class=\\\"fl\\\" href=\\\"/search?q=xkbcomp+alt+gr&amp;newwindow=1&amp;safe=off&amp;ei=QyS5Y_SSL5HO5OUPp_mggAY&amp;start=40&amp;sa=N&amp;ved=2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ8tMDegQIHRAK\\\"\\\n        ><span class=\\\"SJajHc NVbCr\\\" style=\\\"background:url(/images/nav_logo321.webp)\\\n        \\ no-repeat;background-position:-74px 0;background-size:167px;width:20px\\\"\\\n        ></span>5</a></td><td><a aria-label=\\\"Page 6\\\" class=\\\"fl\\\" href=\\\"/search?q=xkbcomp+alt+gr&amp;newwindow=1&amp;safe=off&amp;ei=QyS5Y_SSL5HO5OUPp_mggAY&amp;start=50&amp;sa=N&amp;ved=2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ8tMDegQIHRAM\\\"\\\n        ><span class=\\\"SJajHc NVbCr\\\" style=\\\"background:url(/images/nav_logo321.webp)\\\n        \\ no-repeat;background-position:-74px 0;background-size:167px;width:20px\\\"\\\n        ></span>6</a></td><td><a aria-label=\\\"Page 7\\\" class=\\\"fl\\\" href=\\\"/search?q=xkbcomp+alt+gr&amp;newwindow=1&amp;safe=off&amp;ei=QyS5Y_SSL5HO5OUPp_mggAY&amp;start=60&amp;sa=N&amp;ved=2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ8tMDegQIHRAO\\\"\\\n        ><span class=\\\"SJajHc NVbCr\\\" style=\\\"background:url(/images/nav_logo321.webp)\\\n        \\ no-repeat;background-position:-74px 0;background-size:167px;width:20px\\\"\\\n        ></span>7</a></td><td><a aria-label=\\\"Page 8\\\" class=\\\"fl\\\" href=\\\"/search?q=xkbcomp+alt+gr&amp;newwindow=1&amp;safe=off&amp;ei=QyS5Y_SSL5HO5OUPp_mggAY&amp;start=70&amp;sa=N&amp;ved=2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ8tMDegQIHRAQ\\\"\\\n        ><span class=\\\"SJajHc NVbCr\\\" style=\\\"background:url(/images/nav_logo321.webp)\\\n        \\ no-repeat;background-position:-74px 0;background-size:167px;width:20px\\\"\\\n        ></span>8</a></td><td><a aria-label=\\\"Page 9\\\" class=\\\"fl\\\" href=\\\"/search?q=xkbcomp+alt+gr&amp;newwindow=1&amp;safe=off&amp;ei=QyS5Y_SSL5HO5OUPp_mggAY&amp;start=80&amp;sa=N&amp;ved=2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ8tMDegQIHRAS\\\"\\\n        ><span class=\\\"SJajHc NVbCr\\\" style=\\\"background:url(/images/nav_logo321.webp)\\\n        \\ no-repeat;background-position:-74px 0;background-size:167px;width:20px\\\"\\\n        ></span>9</a></td><td><a aria-label=\\\"Page 10\\\" class=\\\"fl\\\" href=\\\"/search?q=xkbcomp+alt+gr&amp;newwindow=1&amp;safe=off&amp;ei=QyS5Y_SSL5HO5OUPp_mggAY&amp;start=90&amp;sa=N&amp;ved=2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ8tMDegQIHRAU\\\"\\\n        ><span class=\\\"SJajHc NVbCr\\\" style=\\\"background:url(/images/nav_logo321.webp)\\\n        \\ no-repeat;background-position:-74px 0;background-size:167px;width:20px\\\"\\\n        ></span>10</a></td><td aria-level=\\\"3\\\" class=\\\"d6cvqb BBwThe\\\" role=\\\"heading\\\"\\\n        ><a href=\\\"/search?q=xkbcomp+alt+gr&amp;newwindow=1&amp;safe=off&amp;ei=QyS5Y_SSL5HO5OUPp_mggAY&amp;start=10&amp;sa=N&amp;ved=2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ8NMDegQIHRAW\\\"\\\n        \\ id=\\\"pnnext\\\" style=\\\"text-align:left\\\"><span class=\\\"SJajHc NVbCr\\\" style=\\\"\\\n        background:url(/images/nav_logo321.webp) no-repeat;background-position:-96px\\\n        \\ 0;background-size:167px;width:71px\\\"></span><span style=\\\"display:block;margin-left:53px\\\"\\\n        >Berikutnya</span></a></td></tr></table></div></div></div><div data-hveid=\\\"\\\n        CCIQBQ\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQh6kJegQIIhAF\\\"></div><div\\\n        \\ jscontroller=\\\"GU4Gab\\\" style=\\\"display:none\\\" data-pcs=\\\"0\\\" jsaction=\\\"\\\n        rcuQ6b:npT2md\\\"></div><div role=\\\"navigation\\\"><span id=\\\"xjs\\\"></span><div\\\n        \\ id=\\\"gfn\\\"></div><span id=\\\"fvf\\\"></span></div></div></div><style>.Tg0csd{bottom:0;left:0;position:fixed;right:0;z-index:126}</style><div\\\n        \\ class=\\\"Tg0csd\\\"><div jscontroller=\\\"tboZfc\\\" jsdata=\\\"C4mkuf;_;CQYHzA\\\"\\\n        \\ jsaction=\\\"rcuQ6b:npT2md\\\"></div></div><style>.TCIIWe{padding-top:12px}.f6F9Be{position:absolute;bottom:0;width:100%}.fbar\\\n        \\ a{text-decoration:none;white-space:nowrap}.fbar{margin-left:-27px}.Fx4vi{padding-left:27px;margin:0\\\n        \\ !important}#fsl{white-space:nowrap}.f6F9Be{background:#f2f2f2;line-height:40px;padding-bottom:12px}.f6F9Be.TrMVnc{padding-top:12px}.B4GxFc{margin-left:var(--center-abs-margin);}.fbar\\\n        \\ p,.fbar a{color:#70757a}.fbar a:hover{color:#4d5156}.fbar{font-size:14px}.b0KoTc{color:#70757a;padding-left:27px}.b2hzT{border-bottom:1px\\\n        \\ solid #dadce0}.Q8LRLc{font-size:15px}.unknown_loc{background:#70757a}.smiUbb\\\n        \\ img{margin-right:4px}.smiUbb{margin-left:var(--center-abs-margin);line-height:15px;color:#70757a;}#swml{display:inline-block;margin-left:13px;padding-left:16px;border-left:1px\\\n        \\ solid #dadce0}.KwU3F{color:#1a0dab}.GNm3Qb{display:inline-block}.xSQxL{color:#1a0dab;cursor:pointer;display:inline-block}.HDOrGf{line-height:40px}.EYqSq{margin:6px\\\n        \\ 4px 9px 0;border-radius:100%;display:inline-block;height:10px;vertical-align:middle;width:10px}.dfB0uf{color:#4d5156;font-weight:bold}</style><div\\\n        \\ id=\\\"bfoot\\\"><span style=\\\"display:none\\\"><span jscontroller=\\\"DhPYme\\\"\\\n        \\ style=\\\"display:none\\\" data-du=\\\"1\\\" data-mmcnt=\\\"100\\\" jsaction=\\\"rcuQ6b:npT2md\\\"\\\n        ></span></span></div><div id=\\\"sfooter\\\" role=\\\"contentinfo\\\" data-hveid=\\\"\\\n        CAIQAA\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQpyp6BAgCEAA\\\"><h1 class=\\\"\\\n        Uo8X3b OhScic zsYMMe\\\">Link Footer</h1><div id=\\\"footcnt\\\"><div class=\\\"TCIIWe\\\"\\\n        \\ style=\\\"height:106px\\\" id=\\\"fbarcnt\\\"><div class=\\\"f6F9Be TrMVnc\\\" id=\\\"\\\n        fbar\\\"><div jscontroller=\\\"OESk0e\\\" jsmodel=\\\"CgfbTd\\\" jsdata=\\\"v00nOb;_;CQYHx0\\\"\\\n        \\ jsaction=\\\"rcuQ6b:npT2md\\\"></div><div class=\\\"fbar b2hzT\\\"><div class=\\\"\\\n        b0KoTc B4GxFc\\\"><span class=\\\"Q8LRLc\\\">Indonesia</span><div class=\\\"fbar smiUbb\\\"\\\n        \\ id=\\\"swml\\\"><div jscontroller=\\\"qcH9Lc\\\" jsdata=\\\"z6bOeb;_;CQYHx4\\\" jsaction=\\\"\\\n        oEnJg:CEnhyd;gJk92:b6DXXd;gfszqc:BGFS9\\\"><div class=\\\"rwA8ec HDOrGf GNm3Qb\\\"\\\n        \\ style=\\\"white-space:normal\\\"><a jsname=\\\"gXWYVe\\\" href=\\\"#\\\" style=\\\"white-space:normal\\\"\\\n        \\ data-biw=\\\"1440\\\" jsaction=\\\"click:HTIlC\\\" role=\\\"button\\\" tabindex=\\\"0\\\"\\\n        \\ data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQuZ0HegQIAhAC\\\"><div class=\\\"\\\n        GNm3Qb\\\"><span class=\\\"EYqSq unknown_loc\\\"></span><span class=\\\"dfB0uf\\\">Samarinda,\\\n        \\ Kota Samarinda, Kalimantan Timur</span></div><div class=\\\"GNm3Qb\\\"><span\\\n        \\ id=\\\"VdZal\\\">&nbsp;-&nbsp;</span><span class=\\\"KwU3F\\\"><span>Dari alamat\\\n        \\ IP Anda</span></span></div></a><span id=\\\"tsuid_11\\\"></span></div><span\\\n        \\ id=\\\"RYW0de\\\">&nbsp;-&nbsp;</span><update-location class=\\\"xSQxL HDOrGf\\\"\\\n        \\ jscontroller=\\\"KgxeNb\\\" data-eom-state=\\\"1\\\" role=\\\"button\\\" tabindex=\\\"\\\n        0\\\" jsaction=\\\"click:Q1u0zb;hrYh4e:wpAMHe;\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQpLkCegQIAhAE\\\"\\\n        >Perbarui lokasi<span id=\\\"tsuid_14\\\"></span><span id=\\\"tsuid_18\\\"></span></update-location></div></div></div></div><div\\\n        \\ class=\\\"fbar\\\"><span class=\\\"B4GxFc\\\"><span id=\\\"fsl\\\"><a class=\\\"Fx4vi\\\n        \\ wHYlTd ZYHQ7e\\\" href=\\\"https://support.google.com/websearch/?p=ws_results_help&amp;hl=id&amp;fg=1\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw0GqjNput44fZ6ZBs1mehdu\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ8KwCegQIAhAK\\\">Bantuan</a><a href=\\\"#\\\" class=\\\"\\\n        Fx4vi\\\" data-bucket=\\\"websearch\\\" data-psd-ssc=\\\"0\\\" id=\\\"dk2qOd\\\" target=\\\"\\\n        _blank\\\" jsaction=\\\"trigger.YcfJ\\\" data-ved=\\\"2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQLnoECAIQCw\\\"\\\n        >Kirim masukan</a><a class=\\\"Fx4vi wHYlTd ZYHQ7e\\\" href=\\\"https://policies.google.com/privacy?hl=id&amp;fg=1\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw0D-4tgA9XfVc_ckZU4AKhd\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ8awCegQIAhAM\\\">Privasi</a><a class=\\\"Fx4vi\\\n        \\ wHYlTd ZYHQ7e\\\" href=\\\"https://policies.google.com/terms?hl=id&amp;fg=1\\\"\\\n        \\ data-jsarwt=\\\"1\\\" data-usg=\\\"AOvVaw0D_z39WrOW3slg1l1PkhOH\\\" data-ved=\\\"\\\n        2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ8qwCegQIAhAN\\\">Persyaratan</a></span></span></div></div></div></div></div><script\\\n        \\ nonce=\\\"fiRsZQKlRm7AeAysGAgLGA\\\">(function(){(function(){var d=Date.now(),a=google.c.sxs?\\\"\\\n        load2\\\":\\\"load\\\";if(google.timers&&google.timers[a].t){for(var b=document.getElementsByTagName(\\\"\\\n        img\\\"),e=0,c=void 0;c=b[e++];)google.c.setup(c,!1,-1);google.c.frt=!1;google.c.e(a,\\\"\\\n        imn\\\",String(b.length));google.c.ubr(!0,d);google.c.glu&&google.c.glu();google.rll(window,!1,function(){google.tick(a,\\\"\\\n        ol\\\");google.c.u(\\\"pr\\\",a)})}})();}).call(this);</script></div></div><script\\\n        \\ nonce=\\\"fiRsZQKlRm7AeAysGAgLGA\\\">(function(){google.xjs={ck:'xjs.s.Um5rIh7eQx4.L.F4.O',cs:'ACT90oGcqZypAIpN_wO6mRlGU3IQxLCmRg',excm:['gVl0O','ZrXR8b','AOTkuc','Mvtsf','NmR9jd','QFbVC','SKZSKc','jkRPje','y6Ihab']};})();</script><!--\\\n        \\ cctlcm 5 cctlcm --><div id=\\\"_QyS5Y_SSL5HO5OUPp_mggAY_25\\\"></div><style>.kur4we{display:none}.RTZ84b{color:#70757a;cursor:pointer;padding-right:8px}.c2xzTb\\\n        \\ .RTZ84b{padding-top:1px;padding-right:4px}.XEKxtf{color:#70757a;float:right;font-size:12px;line-height:16px;padding-bottom:4px}.CvDJxb.minidiv{margin-top:0}#gb{min-width:unset;position:relative}.minidiv\\\n        \\ #gb{top:2px}#gba{display:none}.Q3DXx #gb>div{padding-left:0}.minidiv .RNNXgb{height:32px;border-radius:16px;background:#fff;margin:10px\\\n        \\ 0 0;box-shadow:none;border:1px solid #dfe1e5}.emcav.emcat .RNNXgb{border-bottom-left-radius:24px;border-bottom-right-radius:24px}.minidiv\\\n        \\ .emcav.emcat .RNNXgb{border-bottom-left-radius:16px;border-bottom-right-radius:16px}.minidiv\\\n        \\ .SDkEP{padding-top:0}.FgNLaf{display:none}.minidiv .logo{padding:0 32px}.emcav.A8SBwf.h3L8Ub{z-index:989}.minidiv\\\n        \\ .iblpc{margin-top:0}.CKb9sd{background:none;display:flex;flex:0 0 auto}.minidiv\\\n        \\ .jfN4p{height:28px;width:86px}.minidiv .gLFyf{padding:0;margin-top:-32px;line-height:32px}.gLFyf.CJxXMe{margin-top:0}.VZUv6d{font-size:11px;font-weight:bold;white-space:nowrap;color:#fff;line-height:29px;padding:0\\\n        \\ 10px}.vT5nhd{height:0;position:fixed;z-index:999}.ZtLxGf{box-sizing:border-box;visibility:hidden;display:inline-block}.lnctfd{animation:g-snackbar-hide\\\n        \\ 400ms cubic-bezier(.4,0,.2,1) both;animation:g-snackbar-hide 400ms cubic-bezier(.4,0,.2,1)\\\n        \\ both;visibility:inherit}.ZWC4b{animation:g-snackbar-show 500ms cubic-bezier(.4,0,.2,1)\\\n        \\ both;animation:g-snackbar-show 500ms cubic-bezier(.4,0,.2,1) both;visibility:inherit}.BDp8nf{margin-right:-8px}.z5QvOe\\\n        \\ .Xb004{display:block;padding:8px 0}.z5QvOe .BDp8nf{margin-left:0}.z5QvOe\\\n        \\ .BDp8nf g-flat-button{padding-left:0}.minidiv .dRYYxd{margin-top:0}.minidiv\\\n        \\ .vOY7J{line-height:32px}.minidiv .ExCKkf{width:20px}.minidiv .nDcEnd{line-height:32px}.minidiv\\\n        \\ .Gdd5U{width:20px;height:20px}.minidiv .Tg7LZd{height:32px;line-height:32px}.minidiv\\\n        \\ .Tg7LZd .zgAlFc{height:20px;width:20px}.minidiv .Tg7LZd svg{height:20px;width:20px}.h3L8Ub\\\n        \\ .rLrQHf{padding-bottom:16px}.h3L8Ub .rLrQHf{margin-right:30px}.h3L8Ub .rLrQHf{min-width:47%;width:47%}.S3nFnd{display:flex}.lh87ke:link,.lh87ke:visited{color:#1a0dab;cursor:pointer;font:11px\\\n        \\ arial,sans-serif;padding:0 5px;text-decoration:none;flex:auto;align-self:flex-end;margin:0\\\n        \\ 16px 5px 0}.lh87ke:hover{text-decoration:underline}.sb7{background:url()\\\n        \\ no-repeat ;background-size:;min-height:0px;min-width:0px;height:0px;width:0px}.sb27{background:url(/images/searchbox/desktop_searchbox_sprites318_hr.webp)\\\n        \\ no-repeat 0 -21px;background-size:20px;min-height:20px;min-width:20px;height:20px;width:20px}.sb43{background:url(/images/searchbox/desktop_searchbox_sprites318_hr.webp)\\\n        \\ no-repeat 0 0;background-size:20px;min-height:20px;min-width:20px;height:20px;width:20px}.sb53.sb53{padding:0\\\n        \\ 4px;margin:0}.Ye4jfc{flex-direction:row;flex-wrap:wrap}.K2P9Ob{padding:16px\\\n        \\ 0 6px 0}.sbic.vYOkbe{background:center/contain no-repeat;border-radius:4px;min-height:32px;min-width:32px;margin:4px\\\n        \\ 7px 4px -5px;}.sbre .wM6W7d{line-height:18px}.minidiv .wM6W7d{font-size:14px}.WggQGd{color:#52188c}.h3L8Ub\\\n        \\ .yMAEcf{border-radius:100px;box-sizing:border-box;display:flex;min-height:40px;margin:4px\\\n        \\ 0 4px 16px;width:396px}@media (forced-colors:none){.h3L8Ub .yMAEcf{background:#f8f9fa}}.h3L8Ub\\\n        \\ .yMAEcf{width:fit-content}.minidiv .h3L8Ub .yMAEcf .wM6W7d{font-size:16px}@media\\\n        \\ (forced-colors:none){.h3L8Ub .yMAEcf.sbhl{background:#e8eaed}}.sbhl{background:#f8f9fa;}@media\\\n        \\ (forced-colors:active){.sbhl{background-color:highlight}}.mus_pc{display:block;margin:6px\\\n        \\ 0}.mus_il{font-family:Arial,Helvetica Neue Light,Helvetica Neue,Helvetica;padding-top:7px;position:relative}.mus_il:first-child{padding-top:0}.mus_il_at{margin-left:10px}.mus_il_st{right:52px;position:absolute}.mus_il_i{align:left;margin-right:10px}.mus_it3{margin-bottom:3px;max-height:24px;vertical-align:bottom}.mus_it5{height:24px;width:24px;vertical-align:bottom;margin-left:10px;margin-right:10px;transform:rotate(90deg)}.mus_tt3{color:#767676;font-size:12px;vertical-align:top}.mus_tt5{color:#d93025;font-size:14px}.mus_tt6{color:#188038;font-size:14px}.mus_tt8{font-size:16px;font-family:Arial,sans-serif}.mus_tt17{color:#212121;font-size:20px}.mus_tt18{color:#212121;font-size:28px}.mus_tt19{color:#767676;font-size:12px}.mus_tt20{color:#767676;font-size:14px}.mus_tt23{color:#767676;font-size:18px}.JCHpcb:hover{color:#1558d6;text-decoration:underline;}.JCHpcb{color:#70757a;font:13px\\\n        \\ arial,sans-serif;cursor:pointer;align-self:center}.h3L8Ub.sTd96e .IDVnvc{margin-right:calc(25%\\\n        \\ - 113px)}.cRV9hb .pcTkSc .ClJ9Yb.ENMKxf span{-webkit-line-clamp:1}.aVbWac\\\n        \\ .sbic.vYOkbe{height:90px;width:90px;border-radius:12px;margin:0}.P8jkh{border-radius:8px;background:rgba(32,33,36,0.04);height:90px;width:90px;position:relative;z-index:2;margin:0}.IpDT1d{position:relative;display:inline-block}.fLciMb{border-radius:50%;color:#5f6368;cursor:pointer;height:24px;margin-top:4px;padding:8px;width:24px}.minidiv\\\n        \\ .fLciMb{margin-top:6px}.fLciMb:hover{background-color:rgba(218,220,224,.5);text-decoration:none}.ZOyvub{visibility:hidden;position:absolute;top:50px;padding:5px\\\n        \\ 6px;background-color:#55524d;color:#f8f9fa;border-radius:4px;font-size:12px;letter-spacing:1px;left:50%;transform:translateX(-50%);width:max-content}#gb{height:0;padding-left:16px;padding-right:16px}.wIV7Db{visibility:hidden}.mn51Ef{margin-left:5px;vertical-align:text-bottom;}.cF4V5c{background-color:#fff}.cF4V5c\\\n        \\ g-menu-item{display:block;font-size:14px;line-height:23px;white-space:nowrap}.cF4V5c\\\n        \\ g-menu-item a,.cF4V5c .y0fQ9c{display:block;padding-top:4px;padding-bottom:4px;cursor:pointer}.cF4V5c\\\n        \\ g-menu-item a,.cF4V5c g-menu-item a:visited,.cF4V5c g-menu-item a:hover{text-decoration:inherit;color:inherit}.zriOQb\\\n        \\ g-menu-item{color:#5f6368;}.zriOQb g-menu-item a,.zriOQb .y0fQ9c{line-height:16px;padding-top:8px;padding-bottom:8px;}.EwsJzb{display:block}.B8Kd8d{position:absolute}.sAKBe{border-radius:8px;box-shadow:0\\\n        \\ 2px 10px 0 rgba(0,0,0,0.2)}.gLSAk{border:none;display:block;outline:none;}.rShyOb{white-space:nowrap}.gLSAk{border-radius:8px}.gLSAk{padding:5px\\\n        \\ 0}.ErsxPb{display:block;position:relative}.znKVS{padding:0 16px;vertical-align:middle}.znKVS.tnhqA{padding:0}.tnhqA>*{padding:0\\\n        \\ 16px}.ohSfHb .znKVS{padding-left:28px}.ErsxPb:hover{cursor:pointer}.ErsxPb,.fbKdEb:hover{cursor:default}.dPaec,g-menu-item[disabled]{pointer-events:none;cursor:default}.dPaec{border-top:1px\\\n        \\ solid;height:0;margin:5px 0}.znKVS{line-height:23px}.fbKdEb{background-image:url(//ssl.gstatic.com/ui/v1/menu/checkmark2.png);background-repeat:no-repeat;background-position:left\\\n        \\ center}.gvybPb,.ErsxPb:active{background-color:rgba(0,0,0,0.1)}.dPaec,g-menu-item[disabled]{color:#dadce0\\\n        \\ !important}.dPaec{border-top-color:#dadce0}.hdtb-tl-sel{border:1px solid\\\n        \\ #dadce0;box-shadow:inset 0 1px 2px 0 rgba(0,0,0,0.1);background:linear-gradient(top,#f8f9fa,#dadce0);}.Lj8KXd\\\n        \\ .QBbvme{margin-top:44px}.yyoM4d{top:58px;padding-top:3px;padding-bottom:7px;top:0;top:68px;}.hdtb-mn-hd{color:#70757a;display:inline-block;position:relative;padding-top:0;padding-bottom:0;padding-right:18px;padding-left:12px;line-height:22px;cursor:pointer}.hdtb-mn-hd:hover{color:#202124}.hdtb-mn-hd:hover\\\n        \\ .gTl8xb{border-color:#202124 transparent}.hdtb-mn-hd:active{color:#1a73e8}.hdtb-mn-hd:active\\\n        \\ .gTl8xb{border-color:#1a73e8 transparent}.LkcePc{display:inline-block;width:var(--center-abs-margin);}.nvELY{background-position:left\\\n        \\ center;background-repeat:no-repeat;background-image:url(//ssl.gstatic.com/ui/v1/menu/checkmark.png)}.Tlae9d\\\n        \\ a,.Tlae9d .y0fQ9c{padding-left:32px;padding-right:32px}.KTBKoe{display:inline-block;padding-right:6px;white-space:nowrap}.hdtb-mn-hd.Yg3U7e{padding-left:0}.gTl8xb{border-color:#70757a\\\n        \\ transparent;border-style:solid;border-width:5px 4px 0 4px;width:0;height:0;margin-left:-2px;top:50%;margin-top:-2px;position:absolute}.T3kYXe,.OouJcb,.rzG2be{color:#202124}.OouJcb,.rzG2be{background-color:#fff;border:1px\\\n        \\ solid #dadce0;border-radius:1px;font-size:13px;height:17px;left:50px;line-height:17px;margin:0\\\n        \\ 4px;padding:5px;position:absolute;width:84px}.OouJcb:focus,.rzG2be:focus{border:1px\\\n        \\ solid #4285f4;box-shadow:inset 0 1px 2px rgba(0,0,0,.3);outline:none}.J6UZg\\\n        \\ .goog-date-picker{left:154px;background-color:#f8f9fa;border-radius:2px;border:none;font-size:12px;outline:none;padding:5px\\\n        \\ 1px 10px;position:absolute;top:61px;user-select:none}.J6UZg .goog-date-picker\\\n        \\ table{padding:0 10px;width:175px}.J6UZg .goog-date-picker table thead td{border-bottom:1px\\\n        \\ solid #dadce0}.J6UZg .goog-date-picker tbody th{width:0}.J6UZg tr.goog-date-picker-head{height:27px}.J6UZg\\\n        \\ tr.goog-date-picker-head td{white-space:nowrap}.J6UZg .goog-date-picker-monthyear{font-size:13px}.J6UZg\\\n        \\ .goog-date-picker tbody{outline:none;font-size:13px}.J6UZg .goog-date-picker\\\n        \\ td,.J6UZg .goog-date-picker th{text-align:center}.J6UZg .goog-date-picker-btn{background:none;border:none;cursor:pointer;font-size:12px;outline:none;padding:0;position:relative;top:-1px}.J6UZg\\\n        \\ .goog-date-picker-btn:not(.suap3e){color:#202124}.J6UZg button.goog-date-picker-btn{font-size:12px;vertical-align:middle}.J6UZg\\\n        \\ .goog-date-picker-wday,.J6UZg .goog-date-picker-date{font-weight:normal;padding:0\\\n        \\ 1px}.J6UZg .goog-date-picker-wday{padding-top:3px;line-height:15px}.J6UZg\\\n        \\ td.goog-date-picker-selected{background-color:#1a73e8;border-radius:2px;color:#fff}.J6UZg\\\n        \\ .goog-date-picker-other-month{color:#dadce0}.J6UZg .goog-date-picker-date{cursor:pointer;width:20px;line-height:15px}.J6UZg\\\n        \\ .goog-date-picker-foot{display:none}.J6UZg td.goog-date-picker-date:hover{background-color:#dadce0;border-radius:2px}.J6UZg\\\n        \\ td.goog-date-picker-year,.J6UZg td.goog-date-picker-month{padding:3px 0}.J6UZg\\\n        \\ button.goog-date-picker-year,.J6UZg button.goog-date-picker-month{color:#000}.J6UZg\\\n        \\ button.goog-date-picker-month{width:77px}.J6UZg button.goog-date-picker-year{width:50px}.J6UZg\\\n        \\ .goog-date-picker-menu{background:#fff;border:solid 1px #4285f4;cursor:pointer;outline:none;position:absolute}.UfY8P\\\n        \\ tr:nth-child(2) .goog-date-picker-other-month{color:#70757a}.T3kYXe{padding:0\\\n        \\ 15px}.suap3e{color:#dadce0;pointer-events:none}.vOvh1b{left:0;background:#fff;height:100%;-ms-filter:\\\"\\\n        progid:DXImageTransform.Microsoft.Alpha(Opacity=75)\\\";opacity:.75;position:fixed;top:0;width:100%;z-index:1000}.J6UZg{left:50%;background:#fff;border:1px\\\n        \\ solid #dadce0;box-shadow:0 4px 16px rgba(0,0,0,.2);height:241px;margin-left:-202px;position:fixed;top:250px;width:373px;z-index:1001}.QIQ7Cc.J6UZg{left:0;margin-left:0}.QIQ7Cc\\\n        \\ .Jy9Ore,.QIQ7Cc .Qtsmnf{left:5px}.QIQ7Cc .NwEGxd{left:-8px}.Gwgzqd{right:11px;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKBAMAAAB/HNKOAAAAElBMVEX////39/e9vb2zs7PCwsLv7++5ffrDAAAAL0lEQVQI12MIEWBgdGVwVmQQMmEQMhJUVmRgVFYyEmBgEDJWZICSEBGILEQlWBcAq64Ft1WDk9gAAAAASUVORK5CYII=)\\\n        \\ center no-repeat;cursor:pointer;height:20px;position:absolute;top:10px;user-select:none;width:20px}.Jy9Ore{left:42px;color:#202124;font-size:16px;position:absolute;top:34px}.Qtsmnf{left:42px;color:#202124;position:absolute}.tmDYm{top:72px}.iWBKNe{top:111px}.OouJcb{top:65px}.rzG2be{top:104px}.NwEGxd{position:relative}.qomYCd{left:50px;background-color:#f8f9fa;border-bottom-left-radius:2px;border-top-left-radius:2px;height:37px;position:absolute;top:61px;transition:top\\\n        \\ .13s linear;width:110px}.KbfSHd{top:100px}.lRiKjb{transition:none}.Ru1Ao{left:54px;position:absolute;top:143px}.BwGU8e{border-radius:2px;border-radius:2px;cursor:pointer;display:inline-block;font-size:11px;font-weight:bold;height:16px;line-height:16px;min-width:54px;padding:6px\\\n        \\ 8px 5px;text-align:center;transition:all 0.218s,visibility 0s;user-select:none}.BwGU8e[disabled]{pointer-events:none;background-color:#f8f9fa;border-color:#f8f9fa;color:#70757a}.fE5Rge{color:#1a73e8;background-color:#fff;border:1px\\\n        \\ solid #dadce0}.fE5Rge:hover{background-color:#f8f9fa;border:1px solid #f8f9fa}.fE5Rge:focus{background-color:#e8f0fe;border:1px\\\n        \\ solid #e8f0fe}.hdtb-ab-o .LHJvCe{opacity:0;top:13px}.CbAZb{background:#fff;border-bottom:1px\\\n        \\ solid #dadce0;bottom:0;overflow-y:auto;position:fixed;right:-360px;top:0;width:360px;font-family:Google\\\n        \\ Sans,arial,sans-serif}.AeB7Sc{background:rgba(32, 33, 36, .6);bottom:0;display:none;left:0;overflow:hidden;position:fixed;right:0;top:0;z-index:9000}.S8wJ3{color:#202124;font-family:Google\\\n        \\ Sans,arial,sans-serif;font-size:28px;margin-bottom:20px;text-align:left}.tGS0Nc{border:1px\\\n        \\ solid #dadce0;border-radius:8px;display:block;font-size:16px;padding:10px;text-align:center}a.tGS0Nc,a:visited.tGS0Nc{color:#1a73e8;text-decoration:none}.bepeF{color:#5f6368;cursor:pointer;display:block;float:right;position:relative;top:-50px}.m0uvVb{font-size:16px;border-top:1px\\\n        \\ solid #dadce0;padding:4px 24px 10px}.kQEH5b{float:right;cursor:pointer}.ZI7elf{color:#4d5156;cursor:pointer}.cQ2awd{padding:20px\\\n        \\ 24px}.kwWBYc{color:#202124;font-family:arial,sans-serif;font-size:20px;font-weight:normal;margin-bottom:8px}.q0yked{color:#4d5156;font-family:arial,sans-serif;margin:0\\\n        \\ -24px}.q0yked:hover{background-color:#f1f3f4}a.tGS0Nc:hover{background-color:rgba(26,\\\n        \\ 115, 232, .08);color:#1967d2}.q0yked a{align-items:center;display:flex;justify-content:space-between;padding:12px\\\n        \\ 24px;text-decoration:none}.fmxhfc{color:#4d5156;margin:20px 0 8px;font-family:arial,sans-serif}.S4xgid{cursor:pointer;padding:12px\\\n        \\ 24px}.uWNHce{color:#4d5156;display:inline-block;margin-left:-16px;margin-bottom:-16px;font-family:arial,sans-serif;width:328px}.UCGAnb{flex:1}.LZTko:hover{background:#f1f3f4;box-shadow:-56px\\\n        \\ 0 #f1f3f4,24px 0 #f1f3f4,-56px -10px 0 #f1f3f4,24px -10px 0 #f1f3f4;cursor:pointer}.UCAEse{height:30px;margin-bottom:5px;margin-top:-5px}.ogD9ue{align-items:center;display:flex}.tkWDZc{font-family:arial,sans-serif;font-size:12px;font-weight:400;line-height:16px}.rhJQGd{color:#70757a;margin-right:6px}.SknMB,.SknMB:visited{align-items:center;color:#1a73e8;display:flex;height:40px}.W3aG6d{align-items:center;display:flex;margin:0\\\n        \\ -24px;min-height:48px}.aoMqnc{animation:loading-pulse 1.25s ease-out 0s\\\n        \\ infinite alternate;background:#f1f3f4;border-radius:4px;height:24px;margin:0\\\n        \\ 24px;opacity:0.2;width:100%}.LWBVLb{display:flex}.JoWl3e{float:right;margin-right:-4px}.jFQOsd{flex:1}.jFQOsd\\\n        \\ span{display:block}.W2x9Sc,.egTMQe{font-size:12px}a.W2x9Sc,a:visited.W2x9Sc{color:#1a73e8}.egTMQe{color:#70757a}.Sqk7pf{color:#5f6368;margin-right:-2px;margin-left:4px}.egTMQe{margin-top:12px}.Sqk7pf{top:8px}.OvQkSb{border-radius:999rem}.dluk7e{margin-left:48px}.GZcH3e{display:block}.de2Dud{max-width:100%;position:relative}.de2Dud:focus{outline:none}.KNNyg{display:inline-block}.dsLpHe{border:solid\\\n        \\ 2px;border-color:#70757a;height:12px;left:16px;position:absolute;transition:border-color\\\n        \\ 0.2s;top:16px;width:12px}.kaAgDc{height:8px;left:20px;position:absolute;top:20px;width:8px;transform:scale(0);transition:transform\\\n        \\ 0.2s}.RvdoFd .kaAgDc{transform:scale(1)}.wT0tpe{height:48px;opacity:0;position:absolute;transition:opacity\\\n        \\ 0.2s;width:48px}.r0zAxe .wT0tpe{opacity:.26}.qwkefd .wT0tpe{opacity:0}.Tro82c\\\n        \\ .RvdoFd .kaAgDc{background-color:rgba(0,0,0,.26)}.Tro82c .dsLpHe{border-color:rgba(0,0,0,.26)}.Tro82c{pointer-events:none}.j0iFNe\\\n        \\ .RvdoFd .dsLpHe{border-color:#4285f4}.j0iFNe .kaAgDc,.j0iFNe .RvdoFd .wT0tpe,.j0iFNe\\\n        \\ .r0zAxe .wT0tpe{background-color:#4285f4}.H8eL7d .dluk7e{padding-bottom:15px;padding-top:15px}.H8eL7d\\\n        \\ .GZcH3e{font-size:16px;line-height:18px}.H8eL7d .de2Dud:not(:last-child){margin-bottom:-16px}.J1lxme{align-items:center;display:flex;flex-flow:row\\\n        \\ wrap;justify-content:space-between}.TkBI4c{align-items:center;display:flex;flex-direction:column;justify-content:space-between}.J1lxme\\\n        \\ .KTXwdc{flex:1 1 100%}.rskU3c .bOiwif{animation:ghost-card-shimmering 1.3s\\\n        \\ infinite;background:-webkit-linear-gradient(left,rgba(255,255,255,0) 0%,\\\n        \\ rgba(255,255,255,.5) 50%, rgba(255,255,255,0) 99%, rgba(255,255,255,0) 100%);background:linear-gradient(to\\\n        \\ right,rgba(255,255,255,0) 0%, rgba(255,255,255,.5) 50%, rgba(255,255,255,0)\\\n        \\ 99%, rgba(255,255,255,0) 100%);bottom:0;right:0;left:0;position:absolute;top:0;transform:translate3d(-100%,0,0)}.rskU3c{overflow-x:hidden;position:relative;width:100%}.G8qI4b{padding:0;}.DYiTxe{padding:0\\\n        \\ 0 12px;border-bottom:1px solid rgba(0,0,0,.05)}.N6dG3e,.e4XSEd{display:block;width:100%}.N6dG3e{background-color:#e8f0fe;height:16px;margin-bottom:8px}.e4XSEd{background-color:rgba(0,0,0,.05);height:12px}.AMbnUc{padding:12px\\\n        \\ 0 0;}.ysLSm{background-color:rgba(0,0,0,.05);display:block;height:60px;width:100%}.v5jHUb{display:none}.FcOujd\\\n        \\ .v5jHUb{display:block;border:1px solid #dadce0;border-top:0;margin-bottom:30px}.ULSxyf{margin-bottom:44px}.hlcw0c{margin-bottom:44px}.D0ONmb\\\n        \\ .hlcw0c:last-child{margin-bottom:0}.FcOujd .ULSxyf:first-child{margin-top:44px}.byrV5b{align-items:center;display:flex;flex-direction:row}.sBJG1d{display:flex;flex-direction:row;justify-content:center}.kDmHO{align-items:center;display:flex;flex-direction:column}.lR4vec{display:flex;flex-direction:column;justify-content:center}.xTEyc{align-items:flex-start;display:flex;flex-direction:row}.OjFzvd{display:flex;flex-direction:row;justify-content:flex-start}.YIPhrb{align-items:flex-start;display:flex;flex-direction:column}.E4bmEc\\\n        \\ .Va021{flex:1 1 100%}.E4bmEc .Y76LGf{flex:1 1 calc(50% - 4px)}.uUuwM{flex:1\\\n        \\ 1 100%;min-width:0}.W27f5e{display:block;height:100%}.gWXfu{margin-top:auto}.NS3Wtc{margin-right:auto}.EwFqud{margin-bottom:auto}.Sth6v{margin-left:auto}.aBeYNc{right:-23px;position:absolute;top:0;width:48px;height:48px}.c2xzTb\\\n        \\ .LC20lb{margin-bottom:0}.MMgsKf{padding-top:2px}.NXKJM{display:-webkit-box;overflow:hidden;-webkit-box-orient:vertical;-webkit-line-clamp:1}.hcV4Re{font-style:normal}.f6F9Be.dc8jac{padding-top:24px}.known_loc{forced-color-adjust:none;background:#4285f4}@media\\\n        \\ (prefers-color-scheme:dark) and (forced-colors:active){.known_loc{background:Highlight}}.lxG8Hd.aID8W{background-color:rgba(0,0,0,0.6);opacity:1;visibility:inherit}.qW28Ef.aID8W{background-color:#000;opacity:0.4;visibility:inherit}.m114nf.aID8W{background-color:#202124;opacity:0.7;visibility:inherit}.xq162b.aID8W{background-color:#000;opacity:0.8;visibility:inherit}.Xz5tfb.aID8W{background-color:#f8f9fa;opacity:0.85;visibility:inherit}.Kg0gUe.aID8W{background-color:#202124;opacity:0.6;visibility:inherit}.NJfJb.aID8W{opacity:1}.bErdLd.aID8W{opacity:1;visibility:inherit}.bErdLd.hFCnyd{cursor:pointer}.bErdLd.hFCnyd\\\n        \\ .NJfJb{cursor:default}.t7xA6{position:fixed;z-index:9997;right:0;bottom:-200px;top:0;left:0;transition:opacity\\\n        \\ 0.25s;opacity:0;visibility:hidden}.NJfJb{border-radius:8px;border-radius:8px;display:inline-block;z-index:9997;background-color:#fff;opacity:0;white-space:normal;overflow:hidden;box-shadow:0px\\\n        \\ 5px 26px 0px rgba(0,0,0,0.22),0px 20px 28px 0px rgba(0,0,0,0.3)}.NJfJb.o1VDwe{background-color:transparent;box-shadow:none}.NJfJb.Sr5CLc{position:relative;vertical-align:middle}.NJfJb.ZbLCRc{position:absolute}.NJfJb.mr5vfb{border:1px\\\n        \\ solid #dadce0;box-shadow:0 2px 4px #dadce0;box-shadow:0 2px 4px #dadce0}.bErdLd{position:fixed;right:0;bottom:0;top:0;left:0;z-index:9997;vertical-align:middle;visibility:hidden;white-space:nowrap;max-height:100%;max-width:100%;overflow-x:hidden;overflow-y:auto}.bErdLd.wwYr3{text-align:center}.bErdLd::after{content:'';display:inline-block;height:100%;vertical-align:middle}.bErdLd{tap-highlight-color:rgba(0,0,0,.0)}.ls8Qne{height:0;opacity:0;position:absolute;width:0}.OxAxec{visibility:hidden}.QVCmK{overflow:hidden}.llJxV\\\n        \\ .NJfJb{max-width:100%}.OosGzb{width:376px}.tYAdEe,.vT5nhd{left:0;right:0}.hObAcc{margin-left:4px;margin-right:4px}.r2fjmd{margin-bottom:0px;margin-top:0px}.gTewb{padding-left:8px;padding-right:8px}.U8shWc{background-color:transparent;border:none;border-radius:8px;border-radius:8px;box-sizing:border-box;cursor:pointer;display:inline-block;font-size:14px;font-weight:500;padding-top:6px;padding-bottom:3px;min-width:88px;position:relative;text-decoration:none\\\n        \\ !important;user-select:none;white-space:nowrap}.U8shWc:disabled,.U8shWc[disabled]:not([disabled=false]){pointer-events:none}.U8shWc.fSXIc{min-width:64px}.U8shWc.hpZDWd{color:#fff}.hpZDWd:hover{background-color:rgba(204,204,204,.15)}.hpZDWd:focus{background-color:rgba(204,204,204,.15)}.hpZDWd:active{background-color:rgba(204,204,204,.25)}.U8shWc.hpZDWd:disabled,.U8shWc.hpZDWd[disabled]:not([disabled=false]){color:rgba(255,255,255,.30)\\\n        \\ !important}.Omzzbd{white-space:normal}.Z7swgb{padding:14px 0}.ozC9Cd{color:#fff;padding-top:4px;margin-bottom:-4px}</style><script\\\n        \\ nonce=\\\"fiRsZQKlRm7AeAysGAgLGA\\\">(function(){google.lliod=false;google.llirm='400px';google.ldi={};google.pim={};(function(){var\\\n        \\ a=google.ldi||{},b;for(b in a)if(a.hasOwnProperty(b)){var c=document.getElementById(b)||document.documentElement.querySelector('img[data-iid=\\\"\\\n        '+b+'\\\"]');c&&Number(c.getAttribute(\\\"data-atf\\\"))&1&&(c.setAttribute(\\\"data-deferred\\\"\\\n        ,\\\"2\\\"),c.src=a[b])};}).call(this);})();</script> <script nonce=\\\"fiRsZQKlRm7AeAysGAgLGA\\\"\\\n        >(function(){google.xjs={ck:'xjs.s.Um5rIh7eQx4.L.F4.O',cs:'ACT90oGcqZypAIpN_wO6mRlGU3IQxLCmRg',excm:['gVl0O','ZrXR8b','AOTkuc','Mvtsf','NmR9jd','QFbVC','SKZSKc','jkRPje','y6Ihab']};})();</script>\\\n        \\ <script nonce=\\\"fiRsZQKlRm7AeAysGAgLGA\\\">(function(){google.kEXPI='0,1361119,6970,1136053,1196191,616,105,415497,45391,17594,32688,25595,12128,66507,2404,33507,60816,29257,6636,46636,23137,68386,24667,40032,4133,85123,38601,17769,16029,30458,11740,702,6384,2886,3882,3057,2953,5122,1966,24,1950,136,494,732,2726,375,2091,229,3605,149,2811,1304,5237,116,249,67,529,27,235,1840,1361,41,129,2119,2628,65,797,1905,1017,492,4285,272,64,1463,153,541,23,66,2445,254,741,782,861,402,185,371,140,47,82,68,728,1339,5272399,134,261,39,5995510,2807583,4,28051084,1404816';})();(function(){var\\\n        \\ u='/xjs/_/js/k\\\\x3dxjs.s.id.fVF4K3cYbAM.O/am\\\\x3dAgFUEK4AcAAAADsACAAkQAEAAAAAFQCAMQDB_ycAAUAaEIMBsEwCSADAEK0fAAAAGAAM4DEAEAAAAAD5A4DzBAAMJiwAAAAAAAAAgGAlCAYXSBAQAAIAAAAAAABQSpPXx5wACA/d\\\\\\\n        x3d1/ed\\\\x3d1/dg\\\\x3d2/rs\\\\x3dACT90oFoKdq0WCbTbfsn5ptB4nfzn6sTIA/m\\\\x3dattn,cdos,cr,dpf,hsm,jsa,d,csi';var\\\n        \\ amd=0;\\nvar d=this||self,e=function(a){return a};var g;var l=function(a,b){this.g=b===h?a:\\\"\\\n        \\\"};l.prototype.toString=function(){return this.g+\\\"\\\"};var h={};\\nfunction\\\n        \\ m(){var a=u;google.lx=function(){p(a);google.lx=function(){}};google.bx||google.lx()}\\n\\\n        function p(a){google.timers&&google.timers.load&&google.tick&&google.tick(\\\"\\\n        load\\\",\\\"xjsls\\\");var b=document;var c=\\\"SCRIPT\\\";\\\"application/xhtml+xml\\\"\\\n        ===b.contentType&&(c=c.toLowerCase());c=b.createElement(c);a=null===a?\\\"null\\\"\\\n        :void 0===a?\\\"undefined\\\":a;if(void 0===g){b=null;var k=d.trustedTypes;if(k&&k.createPolicy){try{b=k.createPolicy(\\\"\\\n        goog#html\\\",{createHTML:e,createScript:e,createScriptURL:e})}catch(q){d.console&&d.console.error(q.message)}g=b}else\\\n        \\ g=b}a=(b=g)?b.createScriptURL(a):a;a=new l(a,h);c.src=\\na instanceof l&&a.constructor===l?a.g:\\\"\\\n        type_error:TrustedResourceUrl\\\";var f,n;(f=(a=null==(n=(f=(c.ownerDocument&&c.ownerDocument.defaultView||window).document).querySelector)?void\\\n        \\ 0:n.call(f,\\\"script[nonce]\\\"))?a.nonce||a.getAttribute(\\\"nonce\\\")||\\\"\\\"\\\n        :\\\"\\\")&&c.setAttribute(\\\"nonce\\\",f);document.body.appendChild(c);google.psa=!0};google.xjsu=u;setTimeout(function(){0<amd?google.caft(function(){return\\\n        \\ m()},amd):m()},0);})();function _DumpException(e){throw e;}\\nfunction _F_installCss(c){}\\n\\\n        (function(){google.jl={blt:'none',chnk:0,dw:false,dwu:true,emtn:0,end:0,ico:false,ikb:0,ine:false,injs:'none',injt:0,injth:0,injv2:false,lls:'viewport',pdt:0,rep:0,snet:true,strt:0,ubm:false,uwp:true};})();(function(){var\\\n        \\ pmc='{\\\\x22aa\\\\x22:{},\\\\x22abd\\\\x22:{\\\\x22abd\\\\x22:false,\\\\x22deb\\\\x22:false,\\\\\\\n        x22det\\\\x22:false},\\\\x22async\\\\x22:{},\\\\x22attn\\\\x22:{},\\\\x22bgd\\\\x22:{\\\\\\\n        x22ac\\\\x22:true,\\\\x22as\\\\x22:true,\\\\x22at\\\\x22:0,\\\\x22ea\\\\x22:true,\\\\x22ed\\\\\\\n        x22:0,\\\\x22ei\\\\x22:true,\\\\x22el\\\\x22:true,\\\\x22ep\\\\x22:true,\\\\x22er\\\\x22:true,\\\\\\\n        x22et\\\\x22:0,\\\\x22eu\\\\x22:false,\\\\x22wl\\\\x22:false},\\\\x22cdos\\\\x22:{\\\\x22cdobsel\\\\\\\n        x22:false},\\\\x22cr\\\\x22:{\\\\x22qir\\\\x22:false,\\\\x22rctj\\\\x22:true,\\\\x22ref\\\\\\\n        x22:false,\\\\x22uff\\\\x22:false},\\\\x22csi\\\\x22:{},\\\\x22d\\\\x22:{},\\\\x22dpf\\\\\\\n        x22:{},\\\\x22foot\\\\x22:{},\\\\x22gf\\\\x22:{\\\\x22pid\\\\x22:196},\\\\x22hsm\\\\x22:{},\\\\\\\n        x22jsa\\\\x22:{\\\\x22csi\\\\x22:true,\\\\x22csir\\\\x22:100},\\\\x22kyn\\\\x22:{},\\\\x22lli\\\\\\\n        x22:{},\\\\x22mu\\\\x22:{\\\\x22murl\\\\x22:\\\\x22https://adservice.google.ru/adsid/google/ui\\\\\\\n        x22},\\\\x22pHXghd\\\\x22:{},\\\\x22sb_wiz\\\\x22:{\\\\x22onf\\\\x22:\\\\x22EAE\\\\x22,\\\\\\\n        x22scq\\\\x22:\\\\x22\\\\x22,\\\\x22stok\\\\x22:\\\\x22yESD3TD4A7zGQIdO2eyiqTq43AI\\\\x22},\\\\\\\n        x22sf\\\\x22:{},\\\\x22tl\\\\x22:{\\\\x22rvkey\\\\x22:\\\\x22AIzaSyC_9Rt88UMjzgg5pIVArnfuIVkJx4zCdTY\\\\\\\n        x22}}';google.pmc=JSON.parse(pmc);})();(function(){var r=['sb_wiz','aa','abd','async','bgd','foot','kyn','lli','mu','pHXghd','sf','tl'];google.plm(r);})();(function(){var\\\n        \\ m=['CQYHxk','[\\\\x22gws-wiz-serp\\\\x22,\\\\x22\\\\x22,\\\\x22xkbcomp alt gr\\\\x22,\\\\\\\n        x22\\\\x22,null,1,0,0,11,\\\\x22id\\\\x22,\\\\x22yESD3TD4A7zGQIdO2eyiqTq43AI\\\\x22,\\\\\\\n        x22\\\\x22,\\\\x22QyS5Y_SSL5HO5OUPp_mggAY\\\\x22,0,\\\\x22id\\\\x22,null,null,null,3,5,null,-1,null,\\\\\\\n        x22\\\\x22,-1,0,null,null,1,0,null,null,1,1800000,1,0,0,10,6,null,null,null,null,null,null,null,null,null,null,null,null,null,1,null,1.15,0,null,null,null,1,null,1,null,1,null,null,null,null,null,null,null,9,1,1,0,null,null,0,null,null,null,null,1,null,null,null,null,null,null,null,0,null,1,1,0,null,\\\\\\\n        x22\\\\x22,0,1,0,-1,null,null,null,0,0,1,null,null,null,null,null,null,0,null,0,0,0,0,\\\\\\\n        x22futura_sug_zp_si_0000000_e\\\\x22,null,null,null,null,\\\\x22\\\\x22,1,1,0]','CQYHxo','[null,null,null,[null,null,[[[3,null,null,[null,[[\\\\\\\n        x22lr_\\\\x22,1,6],[\\\\x22lr_lang_1id\\\\x22,0,6]],0]],[3,null,null,[null,[[\\\\\\\n        x22qdr_\\\\x22,1,6],[\\\\x22qdr_h\\\\x22,0,6],[\\\\x22qdr_d\\\\x22,0,6],[\\\\x22qdr_w\\\\\\\n        x22,0,6],[\\\\x22qdr_m\\\\x22,0,6],[\\\\x22qdr_y\\\\x22,0,6],[\\\\x22cdr_opt\\\\x22,0,1,[1,\\\\\\\n        x22Rentang tertentu...\\\\x22,null,\\\\x22cdr:1,cd_min:x,cd_max:x\\\\x22,\\\\x22\\\\\\\n        x22,\\\\x22text\\\\x22,\\\\x22\\\\x22,\\\\x22\\\\x22,6,null,[[[\\\\x22q\\\\x22,\\\\x22xkbcomp\\\n        \\ alt gr\\\\x22],[\\\\x22newwindow\\\\x22,\\\\x221\\\\x22],[\\\\x22safe\\\\x22,\\\\x22off\\\\\\\n        x22]]],\\\\x22cdr_opt\\\\x22,\\\\x225/23/2004\\\\x22,0]]],1]],[3,null,null,[null,[[\\\\\\\n        x22li_\\\\x22,1,6],[\\\\x22li_1\\\\x22,0,6]],2]]],null,[\\\\x22tbs\\\\x22]]],null,null,[null,[[\\\\\\\n        x22/search?q\\\\\\\\u003dxkbcomp+alt+gr\\\\\\\\u0026newwindow\\\\\\\\u003d1\\\\\\\\u0026safe\\\\\\\n        \\\\u003doff\\\\\\\\u0026source\\\\\\\\u003dlnms\\\\x22,null,null,\\\\x22Semua\\\\x22,1,0,1,null,null,\\\\\\\n        x22WEB\\\\x22,[0,2],null,null,0],[\\\\x22/search?q\\\\\\\\u003dxkbcomp+alt+gr\\\\\\\\\\\n        u0026newwindow\\\\\\\\u003d1\\\\\\\\u0026safe\\\\\\\\u003doff\\\\\\\\u0026source\\\\\\\\u003dlnms\\\\\\\n        \\\\u0026tbm\\\\\\\\u003dshop\\\\x22,null,null,\\\\x22Shopping\\\\x22,0,0,1,null,null,\\\\\\\n        x22SHOPPING\\\\x22,[12,2],null,null,12],[\\\\x22/search?q\\\\\\\\u003dxkbcomp+alt+gr\\\\\\\n        \\\\u0026newwindow\\\\\\\\u003d1\\\\\\\\u0026safe\\\\\\\\u003doff\\\\\\\\u0026source\\\\\\\\u003dlnms\\\\\\\n        \\\\u0026tbm\\\\\\\\u003dnws\\\\x22,null,null,\\\\x22Berita\\\\x22,0,0,1,null,null,\\\\\\\n        x22NEWS\\\\x22,[10,2],null,null,10],[\\\\x22/search?q\\\\\\\\u003dxkbcomp+alt+gr\\\\\\\n        \\\\u0026newwindow\\\\\\\\u003d1\\\\\\\\u0026safe\\\\\\\\u003doff\\\\\\\\u0026source\\\\\\\\u003dlnms\\\\\\\n        \\\\u0026tbm\\\\\\\\u003dvid\\\\x22,null,null,\\\\x22Video\\\\x22,0,0,1,null,null,\\\\x22VIDEOS\\\\\\\n        x22,[13,2],null,null,13],[\\\\x22/search?q\\\\\\\\u003dxkbcomp+alt+gr\\\\\\\\u0026newwindow\\\\\\\n        \\\\u003d1\\\\\\\\u0026safe\\\\\\\\u003doff\\\\\\\\u0026source\\\\\\\\u003dlnms\\\\\\\\u0026tbm\\\\\\\n        \\\\u003disch\\\\x22,null,null,\\\\x22Gambar\\\\x22,0,0,1,null,null,\\\\x22IMAGES\\\\\\\n        x22,[6,2],null,null,6]],[[\\\\x22https://maps.google.ru/maps?newwindow\\\\\\\\u003d1\\\\\\\n        \\\\u0026safe\\\\\\\\u003doff\\\\\\\\u0026q\\\\\\\\u003dxkbcomp+alt+gr\\\\\\\\u0026um\\\\\\\\u003d1\\\\\\\n        \\\\u0026ie\\\\\\\\u003dUTF-8\\\\x22,null,null,\\\\x22Maps\\\\x22,0,0,1,null,null,\\\\x22MAPS\\\\\\\n        x22,[8,2],null,null,8],[\\\\x22/search?q\\\\\\\\u003dxkbcomp+alt+gr\\\\\\\\u0026newwindow\\\\\\\n        \\\\u003d1\\\\\\\\u0026safe\\\\\\\\u003doff\\\\\\\\u0026source\\\\\\\\u003dlnms\\\\\\\\u0026tbm\\\\\\\n        \\\\u003dbks\\\\x22,null,null,\\\\x22Buku\\\\x22,0,0,1,null,null,\\\\x22BOOKS\\\\x22,[2,2],null,null,2],[\\\\\\\n        x22https://www.google.ru/flights?q\\\\\\\\u003dxkbcomp+alt+gr\\\\\\\\u0026newwindow\\\\\\\n        \\\\u003d1\\\\\\\\u0026safe\\\\\\\\u003doff\\\\\\\\u0026source\\\\\\\\u003dlnms\\\\\\\\u0026tbm\\\\\\\n        \\\\u003dflm\\\\x22,null,null,\\\\x22Penerbangan\\\\x22,0,0,1,null,null,\\\\x22FLIGHTS\\\\\\\n        x22,[20,2],null,null,20],[\\\\x22//www.google.com/finance\\\\x22,null,null,\\\\\\\n        x22Keuangan\\\\x22,0,0,1,null,null,\\\\x22FINANCE\\\\x22,[22,2],null,null,22]]]]','CQYHxs','[4,1,null,null,1,1,0,0,0,0,0,0]','CQYHxw','[\\\\\\\n        x22\\\\x22,6,0]','CQYHyw','[null,null,null,null,null,null,[[\\\\x22Sumber\\\\x22,\\\\\\\n        x22unix.stackexchange.com pertama kali diindeks oleh Google lebih dari 10\\\n        \\ tahun lalu\\\\x22,[[null,[[\\\\x22https://unix.stackexchange.com/questions/204634/how-do-i-bind-altgrkey-to-a-symbol\\\\\\\n        x22]],null,null,null,null,null,null,null,null,null,null,[153667,null,2],1],[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[null,\\\\\\\n        x22Koneksi Anda ke situs ini \\\\\\\\u003cb\\\\\\\\u003eaman\\\\\\\\u003c/b\\\\\\\\u003e\\\\\\\n        x22]]],null,null,\\\\x22data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADb0lEQVR4Ae2XA5QbURSGU9vHte2g5iR7lrXdzqRca5K1PZPatq3M1LYOatt2m967yduz5qR+59zBu/+8+z3eRPbLy/+iUDNvwUzp2HuVLV2J6OQaWgt139PTyjXMpFwD9OnTp4BSo+0ODX1ObJBivim70u1lwcH5U2sbdBpXEjTDiVZJMVw7+7HlpBqJC+Ye0bez0irVzGrUNtEMKSHhVNBnLUN6MyONbopYQW8Qho7ynS/2YmLf+k/eWf2nAbC8MFLHC+/B3kwIWX3GOXTNB3j+wnKC1uoA7MSdLSHQdzR8llO0G+pZTlwMEF99Ju2pZF0Ag8BBIBPYcXwnADqD6ID1eoNxsFUBdJwwywxgvJIcQM/vssV61mDsY+0RmGAGAOOMHQkAPE/ENYGL06oAwfyeshDosQXiQmv7CV5OIyJuwPNH1iD2s/YiJKPQi4yCZ8yWB+6RGz+x/M4u6GttO25Te0fXb+mZ3aDgRZIABE3aVhq24mUC0ZOOuUV8LSltGXU/f9fWduNek+OZ6sOe7qmNs7e1dS6SPQCKOUZyQGqfnjc2g4V4HRbhagh+CgF8E3Z80xvEJsl17RwmJBAApXq0XJbNQo7XReRjhQ3TkNRjEAj4CgBOBwfvKejLb6/mHbvtC0IgVDC3ozzR4nAnS1LNshubDGM78rFczWzBJGXZgicxGBw648xZUetu098PAb5hPYzOvuDgVYXR13csfyWXAGQatGyyBg46jYpwJXM+2HWKXq6mV2G2hPQ7Us8ZiQ/ghIW4IAc6T/mQFwCS99WwIPdh7rcbHGQiQWwHBXyHOqOKolslyw9ziR/Wx2uHYWGr8wxASrNOw8sOcZs2ngTwids+J7UGh57ljIeIxmlY+DrJALC4c4eLwRp4aQmAmY9KZ2tuAd9n1AxymWqSEoDkgREWADTceut0nBii58XZltNxrS5BrAO6B8kBhnnM0vlyQmPcOXmGwFOQHELJ5vwO3MfKTKZ8iZp4oXnfcRPvEIBxQSsu6XjxrL9hd12ZVAV/AUHv22PP+qxaVSC5j2TJ7E6Byta5tDRUuQBQqUfXBv/XXwagoOgVsNWfSg+gob2TALoyinSPfIqxsWguSxoc51ShYYQkAA0d1anT8KLJ/XDEO4HvkTlZMUelHPr15F9TOvYV7Es69dvw2//lB1K9l+eHAt7AAAAAAElFTkSuQmCC\\\\\\\n        x22,null,7,null,null,[112175,null,0]]],null,null,null,null,null,0,[[[null,null,null,null,null,[\\\\\\\n        x22\\\\x22,\\\\x22https://webcache.googleusercontent.com/search?q\\\\\\\\u003dcache:NC1LR19_PGoJ:https://unix.stackexchange.com/questions/204634/how-do-i-bind-altgrkey-to-a-symbol\\\\\\\n        \\\\u0026cd\\\\\\\\u003d1\\\\\\\\u0026hl\\\\\\\\u003did\\\\\\\\u0026ct\\\\\\\\u003dclnk\\\\\\\\u0026gl\\\\\\\n        \\\\u003did\\\\x22,null,[null,[32,null,2]],null,null,null,[[\\\\x22Cache\\\\x22]]]]]],null,null,null,\\\\\\\n        x22\\\\x22,0,\\\\x22WEB_RESULT_INNER\\\\x22,null,0]','CQYHy4','[\\\\x22https://unix.stackexchange.com/questions/204634/how-do-i-bind-altgrkey-to-a-symbol\\\\\\\n        x22,\\\\x22How do I bind AltGr+key to a symbol? - Unix Stack Exchange\\\\x22,\\\\\\\n        x22This is easy to do with xmodmap . You can use a keysym directive to change\\\n        \\ the keysyms associated with a key. This affect all the keys that ...\\\\x22,1,\\\\\\\n        x22in\\\\x22,\\\\x22ID\\\\x22,null,\\\\x22/s?tbm\\\\\\\\u003dmap\\\\\\\\u0026gs_ri\\\\\\\\u003dmaps\\\\\\\n        \\\\u0026suggest\\\\\\\\u003dp\\\\x22]','CQYHy0','[null,null,null,null,null,null,[[\\\\\\\n        x22Sumber\\\\x22,\\\\x22askubuntu.com pertama kali diindeks oleh Google lebih\\\n        \\ dari 10 tahun lalu\\\\x22,[[null,[[\\\\x22https://askubuntu.com/questions/107626/remapping-the-altgr-key-to-control-with-setxkbmap\\\\\\\n        x22]],null,null,null,null,null,null,null,null,null,null,[153667,null,2],1],[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[null,\\\\\\\n        x22Koneksi Anda ke situs ini \\\\\\\\u003cb\\\\\\\\u003eaman\\\\\\\\u003c/b\\\\\\\\u003e\\\\\\\n        x22]]],null,null,\\\\x22data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAADbklEQVR4AbxXg7YkWRCs9R7uJ6yqem3bu0ersdUY27Zt27Zt23ZrXtuqmBuDfu3nl+dE42ZWRtS9mVndUmHtanP5JaNWqWDUy1NMeuWEgFsAT+DmGn2MYaxUWmY3vPGKyaDpJQhsAigkbLyG15aInHcjkpkFUEyYmaPIxJCkZ0w6eRiTlAaYizkLT65XpguglDG9UCLMeqUvLygLMHdOcotO+YOBZQlyZCS/We+1l0XA9bIWQA5ypW+9QWlRGgS+7bMF5uSMIVemwrtZGgIidy8ieHJrQXE3kwrSqNP8QEc5CgA54wLEwqCs29Xkg+yJDBr6iyWAnPkCdMru1ADv+olQg37Q1HAAvj2LYW787uNKbvMVgqd3QI2EQIu5HiCvf4WMAjzrxiFquYm8Pv8k5Sdn4g4YUwXYxzWCc0Z78d4Qvm2zQHMvHgD6/AdWIOZzPfaP0sK9bDAsbb9JE+Ca3ws05/T2mXbAmChALfBsTdcROLoe/By6chThOxd4BFlrwDG1DaDG4qIzQE0UkD4w2n0r7mwIfLsXwbdzHqJ59xC6fBj0MSktdOkQd0gIeStJQNRmBKIReDdMynlTiQKiiQ5rp5/FFjsR89gROLIOgcNroPrdcQGEY2IzhG+fhzBBegnWLr/lC3CYwfoI3zgNc4tPswkIJgq4m+h0Lx8GmrX99/E1khOZakUNB4XQ9UlHYBtWi8XLa1i86W2oV+4nzAFlW6KTW0+zdv0jowBLy8+SWjFy7zJCFw+kFaF9rAFQVU7HDI9ozca4AP56ST0CNeCBGvI/Jr5yhC0ZFxC+dRYR4zWwBthiNOeszhnbkHVAs4+snySAnAlHoPkiVaG1ww9gsXm3zIB301S4lwyAbUgN0GcbWQ+e9RPA9vSsGQPb4KpJR2IbXjt/kDV+T7RrB9hG1E3Kf18rfyIlmli8SEc54ZyUYpyG9cuJnM+BOlKqodfPz5v08plyEaCX/5IymUmr+VIERBKDy/2nmXA2L4dd2JmZPeXxXIbww/DZCzlFGA1ya47oMjsGnfy1VJDdN2i+L2F7BnL42kmFsXOV331RjOpGYnZfLXyVK1fY1uwsi+6tD0V3dRTruwQi+TGaVUX+u8ZtezK215FEvFtMOtkk3s8LrBVCu3OqZsthay4/HB+kh602B5jYzzMMBgAAS8cEoV3DMMYAAAAASUVORK5CYII\\\\\\\n        \\\\u003d\\\\x22,null,7,null,null,[112175,null,0]]],null,null,null,null,null,0,[[[null,null,null,null,null,[\\\\\\\n        x22\\\\x22,\\\\x22https://webcache.googleusercontent.com/search?q\\\\\\\\u003dcache:g0lOLCvSMDoJ:https://askubuntu.com/questions/107626/remapping-the-altgr-key-to-control-with-setxkbmap\\\\\\\n        \\\\u0026cd\\\\\\\\u003d2\\\\\\\\u0026hl\\\\\\\\u003did\\\\\\\\u0026ct\\\\\\\\u003dclnk\\\\\\\\u0026gl\\\\\\\n        \\\\u003did\\\\x22,null,[null,[32,null,2]],null,null,null,[[\\\\x22Cache\\\\x22]]]]]],null,null,null,\\\\\\\n        x22\\\\x22,0,\\\\x22WEB_RESULT_INNER\\\\x22,null,0]','CQYHy8','[\\\\x22https://askubuntu.com/questions/107626/remapping-the-altgr-key-to-control-with-setxkbmap\\\\\\\n        x22,\\\\x22Remapping the AltGr key to Control with setxkbmap\\\\x22,\\\\x22Thanks\\\n        \\ to the suggestions of the Xorg community I found out the correct setxkbmap\\\n        \\ command: setxkbmap -option ctrl:ralt_rctrl.\\\\x22,1,\\\\x22in\\\\x22,\\\\x22ID\\\\\\\n        x22,null,\\\\x22/s?tbm\\\\\\\\u003dmap\\\\\\\\u0026gs_ri\\\\\\\\u003dmaps\\\\\\\\u0026suggest\\\\\\\n        \\\\u003dp\\\\x22]','CQYHyE','[null,null,null,null,null,null,[[\\\\x22Sumber\\\\x22,\\\\\\\n        x22bugs.debian.org pertama kali diindeks oleh Google lebih dari 10 tahun lalu\\\\\\\n        x22,[[null,[[\\\\x22https://bugs.debian.org/cgi-bin/bugreport.cgi?bug\\\\\\\\u003d959071\\\\\\\n        x22]],null,null,null,null,null,null,null,null,null,null,[153667,null,2],1],[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[null,\\\\\\\n        x22Koneksi Anda ke situs ini \\\\\\\\u003cb\\\\\\\\u003eaman\\\\\\\\u003c/b\\\\\\\\u003e\\\\\\\n        x22]]],null,null,\\\\x22data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAAG1BMVEUAAAC3ACXAAEDWAADWAFXNADPBAACsAFbBAIGLYPOCAAAACXRSTlMA3/+/v59/X38Ac3jtAAAAwklEQVR4AVyNQQ6CQBAEC1S4ika5Qkvw7h+4E15A8AMbX+DTTbubPVCHmUltb5pIoeu0kqGUaSG/RzoS0hB1/jCmnSKLQkr2mFVnIptiss9lo6trdZ4XrZwewefr7PRKTd3/hZxroYLe50m2M8Z1VBYlwDcJl2wYfHIckqjgifdoEeBAEYUTRccbPgELueXmMWCQbXOndodZPA6NNBNRh/NTIFFaZGLDDrHjNycHNAEmwnqYBAgqYVHEEMEwt9ENygAAJioXQs4qXOcAAAAASUVORK5CYII\\\\\\\n        \\\\u003d\\\\x22,null,7,null,null,[112175,null,0]]],null,null,null,null,null,0,[[[null,null,null,null,null,[\\\\\\\n        x22\\\\x22,\\\\x22https://webcache.googleusercontent.com/search?q\\\\\\\\u003dcache:N25bURB5280J:https://bugs.debian.org/cgi-bin/bugreport.cgi%3Fbug%3D959071\\\\\\\n        \\\\u0026cd\\\\\\\\u003d3\\\\\\\\u0026hl\\\\\\\\u003did\\\\\\\\u0026ct\\\\\\\\u003dclnk\\\\\\\\u0026gl\\\\\\\n        \\\\u003did\\\\x22,null,[null,[32,null,2]],null,null,null,[[\\\\x22Cache\\\\x22]]]]]],null,null,null,\\\\\\\n        x22\\\\x22,0,\\\\x22WEB_RESULT_INNER\\\\x22,null,0]','CQYHyY','[\\\\x22https://bugs.debian.org/cgi-bin/bugreport.cgi?bug\\\\\\\n        \\\\u003d959071\\\\x22,\\\\x22Xephyr and Xnest: AltGr combined keystrokes do not\\\n        \\ work ...\\\\x22,\\\\x22Working AltGr (right Alt key) key on a German keyboard\\\n        \\ currently in Debian/testing Xnest and Xephyr report the following error,\\\n        \\ ...\\\\x22,1,\\\\x22in\\\\x22,\\\\x22ID\\\\x22,null,\\\\x22/s?tbm\\\\\\\\u003dmap\\\\\\\\u0026gs_ri\\\\\\\n        \\\\u003dmaps\\\\\\\\u0026suggest\\\\\\\\u003dp\\\\x22]','CQYHyI','[null,null,null,null,null,null,[[\\\\\\\n        x22Sumber\\\\x22,\\\\x22Red Hat adalah salah satu perusahaan terbesar dan dikenal\\\n        \\ untuk dedikasinya atas perangkat lunak sumber terbuka. Red Hat didirikan\\\n        \\ pada 1993 dan bermarkas di Raleigh, North Carolina, Amerika Serikat. Red\\\n        \\ Hat terkenal karena produknya Red Hat Linux salah satu distro Linux utama.\\\\\\\n        x22,[[null,[[\\\\x22https://bugzilla.redhat.com/show_bug.cgi?id\\\\\\\\u003d508434\\\\\\\n        x22]],null,null,null,null,null,null,null,null,null,null,[153667,null,2],1],[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[null,\\\\\\\n        x22Koneksi Anda ke situs ini \\\\\\\\u003cb\\\\\\\\u003eaman\\\\\\\\u003c/b\\\\\\\\u003e\\\\\\\n        x22]]],null,null,null,[[\\\\x22Wikipedia\\\\x22],null,null,null,[null,null,\\\\\\\n        x22https://id.wikipedia.org/wiki/Red_Hat\\\\x22]],7,null,null,[112174,null,0]]],null,null,null,null,null,0,[[[null,null,null,null,null,[\\\\\\\n        x22\\\\x22,\\\\x22https://webcache.googleusercontent.com/search?q\\\\\\\\u003dcache:NG-jAVxBv_IJ:https://bugzilla.redhat.com/show_bug.cgi%3Fid%3D508434\\\\\\\n        \\\\u0026cd\\\\\\\\u003d4\\\\\\\\u0026hl\\\\\\\\u003did\\\\\\\\u0026ct\\\\\\\\u003dclnk\\\\\\\\u0026gl\\\\\\\n        \\\\u003did\\\\x22,null,[null,[32,null,2]],null,null,null,[[\\\\x22Cache\\\\x22]]]]]],null,null,null,\\\\\\\n        x22\\\\x22,0,\\\\x22WEB_RESULT_INNER\\\\x22,null,0]','CQYHyg','[\\\\x22https://bugzilla.redhat.com/show_bug.cgi?id\\\\\\\n        \\\\u003d508434\\\\x22,\\\\x22508434 \\u2013 (altgr) iso-level-3 / alt-gr broken\\\n        \\ in rawhide\\\\x22,\\\\x22Bug 508434 (altgr) - iso-level-3 / alt-gr broken in\\\n        \\ rawhide ... Output of \\\\x27\\\\\\\\nxkbcomp -xkb :0\\\\x27 (56.90 KB, text/plain)\\\n        \\ 2009-07-10 12:00 UTC, ...\\\\x22,1,\\\\x22in\\\\x22,\\\\x22ID\\\\x22,null,\\\\x22/s?tbm\\\\\\\n        \\\\u003dmap\\\\\\\\u0026gs_ri\\\\\\\\u003dmaps\\\\\\\\u0026suggest\\\\\\\\u003dp\\\\x22]','CQYHyo','[null,null,null,null,null,null,[[\\\\\\\n        x22Sumber\\\\x22,\\\\x22Arch Linux adalah sebuah distribusi Linux untuk komputer\\\n        \\ x86_64 yang didesain untuk menjadi ringan dan sederhana. Cara pengucapan\\\n        \\ Arch Linux adalah [\\u0251\\u02D0rt\\u0283] atau [a\\u02D0t\\u0283].\\\\\\\\nPendekatan\\\n        \\ desain pengembang distro ini berfokus pada kesederhanaan, kebenaran program\\\n        \\ dan minimalisme.\\\\x22,[[null,[[\\\\x22https://bbs.archlinux.org/viewtopic.php?id\\\\\\\n        \\\\u003d62897\\\\x22]],null,null,null,null,null,null,null,null,null,null,[153667,null,2],1],[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[null,\\\\\\\n        x22Koneksi Anda ke situs ini \\\\\\\\u003cb\\\\\\\\u003eaman\\\\\\\\u003c/b\\\\\\\\u003e\\\\\\\n        x22]]],null,null,\\\\x22data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABV0lEQVR4AZyTM0BFYRSAm9qybdu23Zjm2tcam7Jt28Y+V8/Wmu3Tyf8zhu/i4Lv3lx4AKMWq4zjCtZcSo6pGnWDUo486r5PAvuvEzKH75MGtj/qSu8Bx1VrgNUCt9uynglsfBVJmWB1aC4JH6INBw3TwH6JB9ATjvGpPZKKxIGaCWRw1zqiKHGccRowxIHSUDnkLnEaNBKEjdP30WbYkbYYNSdOss4Qp1nvsJBPw/bp4iWuhVoDNNYVLXMCJg8QpFuBfwOdfxKEka57TrlLgM0AzxObz2Akm4AoALqMUjt0n1zgnlkoFLr2UWlw+smkVmSUl1p3H9QoFmLREbojiDSI3QcTvEGtFgk4ECCqJXMVP7B2hIk1SAgy4Ic8yglRCEELEOUi0rGCGKBAgTYg9IbBAmhERMYzSLwE+hP782iYShUF1Byzmp/YR+ZgKSKACJIiukEiDCgGupKpEr/l7+gAAAABJRU5ErkJggg\\\\\\\n        \\\\u003d\\\\\\\\u003d\\\\x22,[[\\\\x22Wikipedia\\\\x22],null,null,null,[null,null,\\\\\\\n        x22https://id.wikipedia.org/wiki/Arch_Linux\\\\x22]],7,null,null,[112174,null,0]]],null,null,null,null,null,0,[],null,null,null,\\\\\\\n        x22\\\\x22,0,\\\\x22WEB_RESULT_INNER\\\\x22,null,0]','CQYHys','[\\\\x22https://bbs.archlinux.org/viewtopic.php?id\\\\\\\n        \\\\u003d62897\\\\x22,\\\\x22alt gr rarely working under X [solved] / Applications\\\n        \\ \\\\\\\\u0026 Desktop ...\\\\x22,\\\\x22this problem occured a month ago, rarely\\\n        \\ at first, and now i often need to start X tens of times before alt gr works.\\\n        \\ i\\\\x27m using x.org 1.4.2. xinit says that ...\\\\x22,1,\\\\x22in\\\\x22,\\\\x22ID\\\\\\\n        x22,null,\\\\x22/s?tbm\\\\\\\\u003dmap\\\\\\\\u0026gs_ri\\\\\\\\u003dmaps\\\\\\\\u0026suggest\\\\\\\n        \\\\u003dp\\\\x22]','CQYHyM','[null,null,null,null,null,null,[[\\\\x22Sumber\\\\x22,\\\\\\\n        x22GitHub adalah layanan hos web bersama untuk proyek pengembangan perangkat\\\n        \\ lunak yang menggunakan sistem kendali versi Git dan layanan hosting internet.\\\n        \\ Hal ini banyak digunakan untuk kode komputer.\\\\x22,[[null,[[\\\\x22https://github.com/termux/termux-x11/issues/37\\\\\\\n        x22]],null,null,null,null,null,null,null,null,null,null,[153667,null,2],1],[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[null,\\\\\\\n        x22Koneksi Anda ke situs ini \\\\\\\\u003cb\\\\\\\\u003eaman\\\\\\\\u003c/b\\\\\\\\u003e\\\\\\\n        x22]]],null,null,\\\\x22data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAM1BMVEX///+6u711eHtaXWFMUFQkKS7j5OQwNTrx8fKRk5bV1tc+Q0esrrDIycqDhomeoaNna24OVeM5AAAA/ElEQVR4AX3T0a6EIAwE0AEY2oLr3v//2iumIaC650UfJkw1BZMQUyZzigEPShQOEhUXJlyIYaaZN1kxVOEDqXAqfCQ6nZ9j46TFRDL7fDxswNbIdBDKBgQeDIciPHwAqJcWBVBHSaQHVoVdBCD0ilVgJ+NFcaGNXUC8HeA2djsSO8Udu4TsVXeJhwx27TVA8IQHjR3a7xka0ttXBB9y9+fLCLsH+X3+DQxjGQwLGyuBSFrIZNs/cJ9vI70BKKRUzeRYkD8OxU+TUuQsPFU6em8mI9Rsg6PL09JWTNal9bWPZrYGpN4uzhLIipldA4aLsovAicSCOy0jrBj+Adb8C4ZUKU58AAAAAElFTkSuQmCC\\\\\\\n        x22,[[\\\\x22Wikipedia\\\\x22],null,null,null,[null,null,\\\\x22https://id.wikipedia.org/wiki/GitHub\\\\\\\n        x22]],7,null,null,[112174,null,0]]],null,null,null,null,null,0,[[[null,null,null,null,null,[\\\\\\\n        x22\\\\x22,\\\\x22https://webcache.googleusercontent.com/search?q\\\\\\\\u003dcache:LTjqjxvizPwJ:https://github.com/termux/termux-x11/issues/37\\\\\\\n        \\\\u0026cd\\\\\\\\u003d6\\\\\\\\u0026hl\\\\\\\\u003did\\\\\\\\u0026ct\\\\\\\\u003dclnk\\\\\\\\u0026gl\\\\\\\n        \\\\u003did\\\\x22,null,[null,[32,null,2]],null,null,null,[[\\\\x22Cache\\\\x22]]]]]],null,null,null,\\\\\\\n        x22\\\\x22,0,\\\\x22WEB_RESULT_INNER\\\\x22,null,0]','CQYHyc','[\\\\x22https://github.com/termux/termux-x11/issues/37\\\\\\\n        x22,\\\\x22Set Keyboard Layout under XWayland #37 - GitHub\\\\x22,\\\\x22Errors\\\n        \\ from xkbcomp are not fatal to the X server ... My Swiss french keyboard\\\n        \\ layout is working (with \\\\\\\\\\\\x22Alt Gr\\\\\\\\\\\\x22 as \\\\\\\\\\\\x22Right Alt\\\\\\\n        \\\\\\\\x22 key).\\\\x22,1,\\\\x22in\\\\x22,\\\\x22ID\\\\x22,null,\\\\x22/s?tbm\\\\\\\\u003dmap\\\\\\\n        \\\\u0026gs_ri\\\\\\\\u003dmaps\\\\\\\\u0026suggest\\\\\\\\u003dp\\\\x22]','CQYHyU','[null,null,null,null,null,null,[[\\\\\\\n        x22Sumber\\\\x22,\\\\x22altgr-weur.eu pertama kali diindeks oleh Google pada April\\\n        \\ 2021\\\\x22,[[null,[[\\\\x22https://altgr-weur.eu/linux.html\\\\x22]],null,null,null,null,null,null,null,null,null,null,[153667,null,2],1],[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[null,\\\\\\\n        x22Koneksi Anda ke situs ini \\\\\\\\u003cb\\\\\\\\u003eaman\\\\\\\\u003c/b\\\\\\\\u003e\\\\\\\n        x22]]],null,null,null,null,7,null,null,[112175,null,0]]],null,null,null,null,null,0,[[[null,null,null,null,null,[\\\\\\\n        x22\\\\x22,\\\\x22https://webcache.googleusercontent.com/search?q\\\\\\\\u003dcache:1lo9ZyaPrqgJ:https://altgr-weur.eu/linux.html\\\\\\\n        \\\\u0026cd\\\\\\\\u003d7\\\\\\\\u0026hl\\\\\\\\u003did\\\\\\\\u0026ct\\\\\\\\u003dclnk\\\\\\\\u0026gl\\\\\\\n        \\\\u003did\\\\x22,null,[null,[32,null,2]],null,null,null,[[\\\\x22Cache\\\\x22]]]]]],null,null,null,\\\\\\\n        x22\\\\x22,0,\\\\x22WEB_RESULT_INNER\\\\x22,null,0]','CQYHyk','[\\\\x22https://altgr-weur.eu/linux.html\\\\\\\n        x22,\\\\x22AltGr-WEur on Linux\\\\x22,\\\\x22https://altgr-weur.eu/map. Next, compile\\\n        \\ and activate the new map for the current display ($DISPLAY):. xkbcomp -w\\\n        \\ 0 -I$HOME/.config/xkb ...\\\\x22,1,\\\\x22in\\\\x22,\\\\x22ID\\\\x22,null,\\\\x22/s?tbm\\\\\\\n        \\\\u003dmap\\\\\\\\u0026gs_ri\\\\\\\\u003dmaps\\\\\\\\u0026suggest\\\\\\\\u003dp\\\\x22]','CQYHyA','[null,null,null,null,null,null,[[\\\\\\\n        x22Sumber\\\\x22,\\\\x22forums.linuxmint.com pertama kali diindeks oleh Google\\\n        \\ lebih dari 10 tahun lalu\\\\x22,[[null,[[\\\\x22https://forums.linuxmint.com/viewtopic.php?t\\\\\\\n        \\\\u003d4162\\\\x22]],null,null,null,null,null,null,null,null,null,null,[153667,null,2],1],[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[null,\\\\\\\n        x22Koneksi Anda ke situs ini \\\\\\\\u003cb\\\\\\\\u003eaman\\\\\\\\u003c/b\\\\\\\\u003e\\\\\\\n        x22]]],null,null,\\\\x22data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAcCAMAAAA3HE0QAAAAolBMVEXv7+/n5+fm5ubp6enq6urw8PD7+/v19fX////+/v7w+eeu332x4IK14Ye5447F6KLi89D9/vzj8dWHzz6Z1F3W772l2m+U0lWAxDuCxzyz3ojz+O2n1Hp+wDuN0Ufx+OqHxUnd8MuQ0U+Px1au1Yar3XiQy1PA5puh2Wh8vDubz2ea0WN6tzybzWiu1IjQ5rrB3aTL5bOl0HqQxFqbymuFvku26t74AAABYklEQVR4AZXRBWKEMBBA0TizToqFdVd05f5Xa3Co9+PwcIQw6YYxppQhXoYYFsanOCOEVYDCV/UMSlEF+oPBYFg0yhtPTAk9RFAJ3qzP2Y4EjlEbuAOvQ8Y9YKwESvm+/wZTq9MEBEE5kLP+fLHQwPf1hUaunk3dpbXs60vkQLdaF8AzAeQme8TNVl/CoBwhogPYabBXagOyLyGfOdYBhAaaoBocJUyXJ8hncnmWOeA8B5cMXAHW/hmkZ9kg7c9gkYNbC+AS9P4D1DcgyED4M4gu6zBcqPX1trS8OPZqwDUIgw0kl8tCp5TyLTtNtw3AAu5BAqtHLbaTNLEawDjEwWMGs00cR1G030dJmrotgCjIMHjOQBenRUOrBjpsQP8VvCJ9INpnzY9+ByAsoP8MguCSVT3oEGrAERUg4/vj9VrXXfs1yIXRA5jNVk0S2kALxkXvQ8XfrAmjlLIP1aBW3Th/B4S7N6UBxW9XAAAAAElFTkSuQmCC\\\\\\\n        x22,null,7,null,null,[112175,null,0]]],null,null,null,null,null,0,[],null,null,null,\\\\\\\n        x22\\\\x22,0,\\\\x22WEB_RESULT_INNER\\\\x22,null,0]','CQYHyQ','[\\\\x22https://forums.linuxmint.com/viewtopic.php?t\\\\\\\n        \\\\u003d4162\\\\x22,\\\\x22Alt gr (right alt key) stopped working + XKB error -\\\n        \\ Linux Mint Forums\\\\x22,\\\\x22Alt gr (right alt key) stopped working + XKB\\\n        \\ error ... circumstanses: - an error in the library libxklavier - an error\\\n        \\ in th X-server (the tools xkbcomp, ...\\\\x22,1,\\\\x22in\\\\x22,\\\\x22ID\\\\x22,null,\\\\\\\n        x22/s?tbm\\\\\\\\u003dmap\\\\\\\\u0026gs_ri\\\\\\\\u003dmaps\\\\\\\\u0026suggest\\\\\\\\u003dp\\\\\\\n        x22]','CQYHzE','[null,null,null,null,null,null,[[\\\\x22Sumber\\\\x22,\\\\x22bugs.freedesktop.org\\\n        \\ pertama kali diindeks oleh Google lebih dari 10 tahun lalu\\\\x22,[[null,[[\\\\\\\n        x22https://bugs.freedesktop.org/show_bug.cgi?id\\\\\\\\u003d19602\\\\x22]],null,null,null,null,null,null,null,null,null,null,[153667,null,2],1],[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[null,\\\\\\\n        x22Koneksi Anda ke situs ini \\\\\\\\u003cb\\\\\\\\u003eaman\\\\\\\\u003c/b\\\\\\\\u003e\\\\\\\n        x22]]],null,null,\\\\x22data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAARVBMVEW6uro7gK5KirRAg7BPjbZblLt/rMtUkLhrn8KkxNrb6PBgmL2pxtvw9fn////T4+3k7vR2psaUutO30OFyo8WOttG+1uQKWAGAAAAAaUlEQVR4AXXPxQHAIBAEwAR3h/5LzUHc9reD3vTOfMsPIDwKJpQBcCGV7h0Z6xyAg5i+Sr07IOgoNEknON7PUCP4AJsj6hDhboBCCS4auqCXf4CQcvuYTp7eoDoXb0Abg5fwCRgfszyyADnFBCTGY3FxAAAAAElFTkSuQmCC\\\\\\\n        x22,null,7,null,null,[112175,null,0]]],null,null,null,null,null,0,[[[null,null,null,null,null,[\\\\\\\n        x22\\\\x22,\\\\x22https://webcache.googleusercontent.com/search?q\\\\\\\\u003dcache:W_MpBv-RifgJ:https://bugs.freedesktop.org/show_bug.cgi%3Fid%3D19602\\\\\\\n        \\\\u0026cd\\\\\\\\u003d9\\\\\\\\u0026hl\\\\\\\\u003did\\\\\\\\u0026ct\\\\\\\\u003dclnk\\\\\\\\u0026gl\\\\\\\n        \\\\u003did\\\\x22,null,[null,[32,null,2]],null,null,null,[[\\\\x22Cache\\\\x22]]]]]],null,null,null,\\\\\\\n        x22\\\\x22,0,\\\\x22WEB_RESULT_INNER\\\\x22,null,0]','CQYHzQ','[\\\\x22https://bugs.freedesktop.org/show_bug.cgi?id\\\\\\\n        \\\\u003d19602\\\\x22,\\\\x22ALT-GR switch on german keyboard layout behaves weird\\\\\\\n        x22,\\\\x22Actually, yes. Please attach the output of \\\\\\\\\\\\x22xkbcomp -xkb\\\n        \\ :0 -\\\\\\\\\\\\x22 after and before running setxkbmap (i.e. once when it works,\\\n        \\ once when it doesn\\\\x27t) ...\\\\x22,1,\\\\x22in\\\\x22,\\\\x22ID\\\\x22,null,\\\\x22/s?tbm\\\\\\\n        \\\\u003dmap\\\\\\\\u0026gs_ri\\\\\\\\u003dmaps\\\\\\\\u0026suggest\\\\\\\\u003dp\\\\x22]','CQYHzI','[null,null,null,null,null,null,[[\\\\\\\n        x22Sumber\\\\x22,\\\\x22Gentoo Linux adalah suatu distribusi Linux yang memakai\\\n        \\ paket sistem manajemen Portage. Manajemen paket ini dirancang untuk modular,\\\n        \\ portabel, mudah ditata, fleksibel, dan dioptimalkan untuk masing-masing\\\n        \\ komputer pengguna.\\\\x22,[[null,[[\\\\x22https://forums.gentoo.org/viewtopic-t-808068-start-0.html\\\\\\\n        x22]],null,null,null,null,null,null,null,null,null,null,[153667,null,2],1],[null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,[null,\\\\\\\n        x22Koneksi Anda ke situs ini \\\\\\\\u003cb\\\\\\\\u003eaman\\\\\\\\u003c/b\\\\\\\\u003e\\\\\\\n        x22]]],null,null,\\\\x22data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgBAMAAACBVGfHAAAAMFBMVEV3WtSJavByWcSAbMejnOW7u/2pqNPT0v308/3h4fz9//yIgpJ+e6ReR6dLP29DMnevEufzAAABXElEQVR4AV3NMUzCQBTG8UNdHXAfyJkwyWJMJyeLkQE3LriHoEjX5sDZwLVxthxhNth9u4aRpRdIGkcubC4mug+I77VUE9/E98ufK8ErZge/9ijuwn8o7l+2mf0HhaMr4QXMLvwWlh+KgDfs4g7278Nw6nHWuNiBFYYggcuqJQDAGuwssVM4eIDttzoBY9VTgFLZB3C7UcAgoVCUxwCP3ajvYgLFSQfgta4RGtcI+g7E4wFnjNmU0MNYYzNNgSWEllYKGvyuy5rDhJRo5UN1ACTn7ObJEErp+t3PgAu9QTiuhRn0BosUznwAIYO+0MuEQABPIsjxRG8MgIXBVMjRi7NEwBcgEFI4emsqhL710u35E+drYwxJrJFIb+AsIDDEzKX0oB+2IwwQuJRy5NUVboTzW877zzqGP2Swmjd5S6sZ7gyU1rGCB3IwsVJqlm+E7zj63OYbYb2FM/n9AJyA/ueDtSh/AAAAAElFTkSuQmCC\\\\\\\n        x22,[[\\\\x22Wikipedia\\\\x22],null,null,null,[null,null,\\\\x22https://id.wikipedia.org/wiki/Gentoo_Linux\\\\\\\n        x22]],7,null,null,[112174,null,0]]],null,null,null,null,null,0,[[[null,null,null,null,null,[\\\\\\\n        x22\\\\x22,\\\\x22https://webcache.googleusercontent.com/search?q\\\\\\\\u003dcache:A0aBH3_SIAwJ:https://forums.gentoo.org/viewtopic-t-808068-start-0.html\\\\\\\n        \\\\u0026cd\\\\\\\\u003d10\\\\\\\\u0026hl\\\\\\\\u003did\\\\\\\\u0026ct\\\\\\\\u003dclnk\\\\\\\\u0026gl\\\\\\\n        \\\\u003did\\\\x22,null,[null,[32,null,2]],null,null,null,[[\\\\x22Cache\\\\x22]]]]]],null,null,null,\\\\\\\n        x22\\\\x22,0,\\\\x22WEB_RESULT_INNER\\\\x22,null,0]','CQYHzM','[\\\\x22https://forums.gentoo.org/viewtopic-t-808068-start-0.html\\\\\\\n        x22,\\\\x22KDE: wrong key mappings with right alt / alt-gr [solved]\\\\x22,\\\\\\\n        x22Errors from xkbcomp are not fatal to the X server (EE) Logitech Logitech\\\n        \\ Illuminated Keyboard: failed to initialize for relative axes.\\\\x22,1,\\\\\\\n        x22in\\\\x22,\\\\x22ID\\\\x22,null,\\\\x22/s?tbm\\\\\\\\u003dmap\\\\\\\\u0026gs_ri\\\\\\\\u003dmaps\\\\\\\n        \\\\u0026suggest\\\\\\\\u003dp\\\\x22]','CQYHzA','[null,null,1,30000,null,null,null,2,null,null,3,null,null,null,null,null,1,null,null,null,1,null,null,[-0.4945727,117.114698],null,null,null,null,null,null,\\\\\\\n        x22604800000\\\\x22,null,1,null,null,null,null,\\\\x221673077827\\\\x22,null,null,null,null,null,1,null,null,[\\\\\\\n        x2286400000\\\\x22,\\\\x22604800000\\\\x22,2],null,null,21600000,null,null,1,null,null,null,null,null,1,null,null,1,1,null,null,1]','CQYHx0','[\\\\\\\n        x22fbsm\\\\x22,2,4,[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19],null,20,1]','CQYHx4','[4,\\\\\\\n        x22AZ3MRNYhMYK_AnYsFRgqgb9MieOWrFkAQJRCdKOzWgBA\\\\x22]','CQYHx8','[1,null,null,1,0,0,0,null,0]','tq7Pxb','[[[\\\\\\\n        x22RxFwtc\\\\x22,null,\\\\x220 4px 16px rgba(0,0,0,0.2)\\\\x22],[\\\\x22aM8S7c\\\\x22,null,\\\\\\\n        x22#666\\\\x22],[\\\\x22tqmosb\\\\x22,null,\\\\x22#fff\\\\x22],[\\\\x22q49bvd\\\\x22,0],[\\\\\\\n        x22vkQXZ\\\\x22,null,\\\\x22#fff\\\\x22],[\\\\x22U2GTk\\\\x22,null,\\\\x22#fff\\\\x22],[\\\\\\\n        x22WgRLme\\\\x22,null,\\\\x22#dadce0\\\\x22],[\\\\x22QcZxSd\\\\x22,null,\\\\x22#3c4043\\\\\\\n        x22],[\\\\x22g4ToDf\\\\x22,null,\\\\x220 1px 2px rgba(60,64,67,.3), 0 2px 6px 2px\\\n        \\ rgba(60,64,67,.15)\\\\x22],[\\\\x22AsC4Mb\\\\x22,null,\\\\x22#9aa0a6\\\\x22],[\\\\x22mub7Fd\\\\\\\n        x22,null,\\\\x22#f1f3f4\\\\x22],[\\\\x22z2SQwf\\\\x22,null,\\\\x22#bdc1c6\\\\x22],[\\\\\\\n        x22ob4Y0c\\\\x22,null,\\\\x22#e8eaed\\\\x22],[\\\\x22M1fk3b\\\\x22,null,\\\\x22#dadce0\\\\\\\n        x22],[\\\\x22gWINCf\\\\x22,null,\\\\x22#9aa0a6\\\\x22],[\\\\x22I6R5lf\\\\x22,null,\\\\x22#f8f9fa\\\\\\\n        x22],[\\\\x22KCMXVb\\\\x22,null,\\\\x22#202124\\\\x22],[\\\\x22LIYDac\\\\x22,null,\\\\x22arial,sans-serif\\\\\\\n        x22],[\\\\x22wUvFOb\\\\x22,0],[\\\\x22a1lsHe\\\\x22,1],[\\\\x22vzRvgb\\\\x22,null,\\\\x22#e8f0fe\\\\\\\n        x22],[\\\\x22HNLwz\\\\x22,null,\\\\x22#d2e3fc\\\\x22],[\\\\x22uD3Lwc\\\\x22,null,\\\\x22#d2e3fc\\\\\\\n        x22],[\\\\x22MLAA8d\\\\x22,null,\\\\x220 1px 2px rgba(66,133,244,.3), 0 1px 3px\\\n        \\ 1px rgba(66,133,244,.15)\\\\x22],[\\\\x22TqDTGf\\\\x22,null,\\\\x22#aecbfa\\\\x22],[\\\\\\\n        x22m7EnTc\\\\x22,null,\\\\x22#8ab4f8\\\\x22],[\\\\x22jyEUXe\\\\x22,null,\\\\x22#d2e3fc\\\\\\\n        x22],[\\\\x22QyzZ8e\\\\x22,null,\\\\x22#174ea6\\\\x22],[\\\\x22CFgsb\\\\x22,null,\\\\x22#1558d6\\\\\\\n        x22],[\\\\x22LrGvF\\\\x22,0],[\\\\x22VXIo7d\\\\x22,null,\\\\x228px\\\\x22],[\\\\x22AoIPu\\\\\\\n        x22,1],[\\\\x22EgTnfe\\\\x22,1],[\\\\x22v4iQCe\\\\x22,null,\\\\x22#4285f4\\\\x22],[\\\\\\\n        x22rZLJJb\\\\x22,null,\\\\x22#fff\\\\x22],[\\\\x22hETIfb\\\\x22,null,\\\\x22#dadce0\\\\\\\n        x22],[\\\\x22IfdAAd\\\\x22,null,\\\\x22#70757a\\\\x22],[\\\\x22Sa67Pc\\\\x22,null,\\\\x22rgba(32,33,36,.28)\\\\\\\n        x22],[\\\\x22bfUEif\\\\x22,0],[\\\\x22mfyQhd\\\\x22,0],[\\\\x22OmYlge\\\\x22,null,\\\\x22#34a853\\\\\\\n        x22],[\\\\x22H4gDmf\\\\x22,null,\\\\x22#ea4335\\\\x22],[\\\\x22KkPbyc\\\\x22,null,\\\\x22#fbbc04\\\\\\\n        x22],[\\\\x22z8cfje\\\\x22,0],[\\\\x22ULXB9b\\\\x22,0],[\\\\x22SA0BPe\\\\x22,0],[\\\\x22MWZX1c\\\\\\\n        x22,null,null,20],[\\\\x22IBWrx\\\\x22,null,null,18],[\\\\x22ziwEW\\\\x22,null,\\\\\\\n        x22#70757a\\\\x22],[\\\\x22Y4x24b\\\\x22,0],[\\\\x22nFFlIb\\\\x22,0],[\\\\x22r9V7hf\\\\\\\n        x22,0],[\\\\x22mBxYPd\\\\x22,null,\\\\x22#f5f5f5\\\\x22],[\\\\x22FqP1Fc\\\\x22,null,\\\\\\\n        x22#000\\\\x22],[\\\\x22rxUBKf\\\\x22,null,\\\\x22#fff\\\\x22],[\\\\x22o28sBd\\\\x22,0],[\\\\\\\n        x22Ydluub\\\\x22,null,\\\\x22#f5f5f5\\\\x22],[\\\\x22GV48mf\\\\x22,null,\\\\x22rgba(0,0,0,.87)\\\\\\\n        x22],[\\\\x22Fcb4A\\\\x22,null,\\\\x22#1a73e8\\\\x22],[\\\\x22X1bUEc\\\\x22,null,\\\\x22arial,sans-serif-medium,sans-serif\\\\\\\n        x22],[\\\\x22LACYrf\\\\x22,0],[\\\\x22JQWqub\\\\x22,null,\\\\x22#ea4335\\\\x22],[\\\\x22GJQmmf\\\\\\\n        x22,null,\\\\x22#34a853\\\\x22],[\\\\x22xNPzic\\\\x22,null,\\\\x22#fff\\\\x22],[\\\\x22FEmOOb\\\\\\\n        x22,null,null,48],[\\\\x22cWwp7b\\\\x22,0],[\\\\x22wsRfI\\\\x22,0],[\\\\x22WkjuOe\\\\\\\n        x22,0],[\\\\x22h6eQZc\\\\x22,0],[\\\\x22b0Jode\\\\x22,0],[\\\\x22WitVqe\\\\x22,1],[\\\\\\\n        x22YjL9Ce\\\\x22,0],[\\\\x22mIjP6d\\\\x22,0],[\\\\x22m8l8td\\\\x22,null,\\\\x22CARET\\\\\\\n        x22],[\\\\x22PngPbb\\\\x22,null,\\\\x22#202124\\\\x22],[\\\\x22V6eh7c\\\\x22,null,null,16],[\\\\\\\n        x22zsYZK\\\\x22,null,\\\\x22#dadce0\\\\x22],[\\\\x22rUXIL\\\\x22,null,\\\\x22\\\\x22],[\\\\\\\n        x22kcrUme\\\\x22,null,null,14],[\\\\x22QuXhMb\\\\x22,0],[\\\\x22QZheGe\\\\x22,null,\\\\\\\n        x22Google Sans,arial,sans-serif-medium,sans-serif\\\\x22],[\\\\x22ZxI7Af\\\\x22,null,\\\\\\\n        x22#D8D7DC\\\\x22],[\\\\x22AgJzQ\\\\x22,null,\\\\x22rgba(255,255,255,0)\\\\x22],[\\\\\\\n        x22FagChc\\\\x22,null,\\\\x22#f1f3f4\\\\x22],[\\\\x22tCGJz\\\\x22,null,\\\\x22#D8D7DC\\\\\\\n        x22],[\\\\x22qtDmFc\\\\x22,null,\\\\x22rgba(255,255,255,0)\\\\x22],[\\\\x22JyBo2c\\\\\\\n        x22,0],[\\\\x22bmQ7Rb\\\\x22,0],[\\\\x22xT28q\\\\x22,1],[\\\\x22VBSc8c\\\\x22,0],[\\\\x22qdoinb\\\\\\\n        x22,null,\\\\x22#70757a\\\\x22],[\\\\x22HjM8R\\\\x22,null,\\\\x22#fff\\\\x22],[\\\\x22tswRXd\\\\\\\n        x22,null,\\\\x22none\\\\x22],[\\\\x22EJG4vf\\\\x22,null,\\\\x22pointer\\\\x22],[\\\\x22BQYKgd\\\\\\\n        x22,null,\\\\x22rgba(0,0,0,.2)\\\\x22],[\\\\x22QU9sdc\\\\x22,null,\\\\x22#202124\\\\x22],[\\\\\\\n        x22bxZRld\\\\x22,null,\\\\x22linear-gradient(90deg, #fff 50%, rgba(255,255,255,0.09)\\\n        \\ 100%)\\\\x22],[\\\\x22ft68ee\\\\x22,null,\\\\x22linear-gradient(270deg, #fff 50%,\\\n        \\ rgba(255,255,255,0.09) 100%)\\\\x22],[\\\\x22xgY1nc\\\\x22,null,\\\\x22#f8f9fa\\\\\\\n        x22],[\\\\x22p1ocJb\\\\x22,null,\\\\x22#f8f9fa\\\\x22],[\\\\x22bDOvJc\\\\x22,0],[\\\\x22HCImye\\\\\\\n        x22,0],[\\\\x22m4Aoxd\\\\x22,0],[\\\\x22SOoLvb\\\\x22,null,null,8],[\\\\x22MRtzJd\\\\\\\n        x22,1],[\\\\x22kjIzLb\\\\x22,1],[\\\\x22klgere\\\\x22,null,\\\\x22invert(1) hue-rotate(180deg)\\\\\\\n        x22],[\\\\x22yqqDTc\\\\x22,0],[\\\\x22uDSxz\\\\x22,null,\\\\x22#dadce0\\\\x22],[\\\\x22oHxv0d\\\\\\\n        x22,null,null,16],[\\\\x22voc95c\\\\x22,0],[\\\\x22I3x8Lb\\\\x22,0],[\\\\x22Rvxsx\\\\\\\n        x22,null,\\\\x221px solid #dadce0\\\\x22],[\\\\x22qmcJmd\\\\x22,null,null,6],[\\\\x22MClBOe\\\\\\\n        x22,null,\\\\x22rgba(0,0,0,0.1)\\\\x22],[\\\\x22SfSmD\\\\x22,null,\\\\x22#dadce0\\\\x22],[\\\\\\\n        x22AP8pqf\\\\x22,null,\\\\x22#dadce0\\\\x22],[\\\\x22sBpVac\\\\x22,null,\\\\x22rgba(0,0,0,.26)\\\\\\\n        x22],[\\\\x22aZFNNe\\\\x22,0],[\\\\x22rzzybc\\\\x22,null,\\\\x22#d93025\\\\x22],[\\\\x22zRpUk\\\\\\\n        x22,null,\\\\x22#4285f4\\\\x22],[\\\\x22JuqxTb\\\\x22,null,\\\\x22#202124\\\\x22],[\\\\\\\n        x22Zun3Ef\\\\x22,0],[\\\\x22ZMIIMe\\\\x22,1],[\\\\x22LVoecd\\\\x22,null,\\\\x22rgba(0,0,0,.16)\\\\\\\n        x22],[\\\\x22yHlFbb\\\\x22,null,\\\\x22rgba(0,0,0,.40)\\\\x22],[\\\\x22OL2x3c\\\\x22,0],[\\\\\\\n        x22wFGKdc\\\\x22,0],[\\\\x22HIMA4e\\\\x22,0],[\\\\x22m2hzy\\\\x22,0],[\\\\x22FydCC\\\\x22,1],[\\\\\\\n        x22wku5sd\\\\x22,0],[\\\\x22pbvXic\\\\x22,1],[\\\\x22rRH6Ed\\\\x22,null,\\\\x22#1a73e8\\\\\\\n        x22],[\\\\x22uXUEhb\\\\x22,0],[\\\\x22mNmrAb\\\\x22,null,\\\\x22#ebebeb\\\\x22],[\\\\x22ptnYGd\\\\\\\n        x22,null,\\\\x22[[[\\\\\\\\\\\\x22box-shadow\\\\\\\\\\\\x22,\\\\\\\\\\\\x22box-shadow\\\\\\\\\\\\x22],[\\\\\\\n        \\\\\\\\x22box-sizing\\\\\\\\\\\\x22,\\\\\\\\\\\\x22box-sizing\\\\\\\\\\\\x22],[\\\\\\\\\\\\x22keyframes\\\\\\\n        \\\\\\\\x22,\\\\\\\\\\\\x22keyframes\\\\\\\\\\\\x22],[\\\\\\\\\\\\x22animation\\\\\\\\\\\\x22,\\\\\\\\\\\\x22animation\\\\\\\n        \\\\\\\\x22],[\\\\\\\\\\\\x22border-radius\\\\\\\\\\\\x22,\\\\\\\\\\\\x22border-radius\\\\\\\\\\\\x22],[\\\\\\\n        \\\\\\\\x22transform\\\\\\\\\\\\x22,\\\\\\\\\\\\x22transform\\\\\\\\\\\\x22],[\\\\\\\\\\\\x22filter\\\\\\\\\\\n        \\\\x22,\\\\\\\\\\\\x22filter\\\\\\\\\\\\x22],[\\\\\\\\\\\\x22flex-direction\\\\\\\\\\\\x22,\\\\\\\\\\\\x22flex-direction\\\\\\\n        \\\\\\\\x22],[\\\\\\\\\\\\x22align-items\\\\\\\\\\\\x22,\\\\\\\\\\\\x22align-items\\\\\\\\\\\\x22],[\\\\\\\n        \\\\\\\\x22flex-grow\\\\\\\\\\\\x22,\\\\\\\\\\\\x22flex-grow\\\\\\\\\\\\x22],[\\\\\\\\\\\\x22flex-shrink\\\\\\\n        \\\\\\\\x22,\\\\\\\\\\\\x22flex-shrink\\\\\\\\\\\\x22],[\\\\\\\\\\\\x22justify-content\\\\\\\\\\\\x22,\\\\\\\n        \\\\\\\\x22justify-content\\\\\\\\\\\\x22],[\\\\\\\\\\\\x22flex\\\\\\\\\\\\x22,\\\\\\\\\\\\x22flex\\\\\\\\\\\n        \\\\x22]]]\\\\x22]]]'];var a=m;window.W_jd=window.W_jd||{};for(var b=0;b<a.length;b+=2)window.W_jd[a[b]]=JSON.parse(a[b+1]);})();(function(){window.WIZ_global_data={\\\"\\\n        GWsdKe\\\":\\\"id-ID\\\",\\\"Im6cmf\\\":\\\"/wizrpcui/_/WizRpcUi\\\",\\\"S06Grb\\\":\\\"\\\",\\\"\\\n        LVIXXb\\\":\\\"1\\\",\\\"w2btAe\\\":\\\"%.@.\\\\\\\"\\\\\\\",\\\\\\\"\\\\\\\",\\\\\\\"0\\\\\\\",null,null,null,1]\\\"\\\n        ,\\\"eptZe\\\":\\\"/wizrpcui/_/WizRpcUi/\\\",\\\"SNlM0e\\\":\\\"\\\",\\\"zChJod\\\":\\\"%.@.null,\\\\\\\n        \\\"https://www.google.ru/log?format\\\\\\\\u003djson\\\\\\\"]\\\",\\\"QrtxK\\\":\\\"0\\\",\\\"\\\n        Yllh3e\\\":\\\"%.@.1673077827772468,112797457,1611152551]\\\"};window.IJ_values={\\\"\\\n        eG8Zqf\\\":1.0,\\\"IvNqzc\\\":true,\\\"ZAVjNb\\\":false,\\\"kRerQb\\\":false,\\\"AoIPu\\\":true,\\\"\\\n        CieUQe\\\":true,\\\"HCMJkf\\\":true,\\\"zNiNDd\\\":false,\\\"EsWLY\\\":false,\\\"XP4Noc\\\"\\\n        :false,\\\"jqcxU\\\":\\\"#4285f4\\\",\\\"toVELc\\\":\\\"#f8f9fa\\\",\\\"V1TJEb\\\":\\\"#1a73e8\\\"\\\n        ,\\\"eavN9c\\\":36,\\\"XuC5Td\\\":24,\\\"ivyWed\\\":28,\\\"psmQyf\\\":6,\\\"osNyZ\\\":1.0,\\\"L6WyEf\\\"\\\n        :true,\\\"tswRXd\\\":\\\"none\\\",\\\"vq4Rhf\\\":true,\\\"mtmrtb\\\":\\\"0 1px 6px rgba(32,33,36,0.28)\\\"\\\n        ,\\\"hOdcKb\\\":false,\\\"vkQXZ\\\":\\\"#fff\\\",\\\"U2GTk\\\":\\\"#fff\\\",\\\"WgRLme\\\":\\\"#dadce0\\\"\\\n        ,\\\"QcZxSd\\\":\\\"#3c4043\\\",\\\"g4ToDf\\\":\\\"0 1px 2px rgba(60,64,67,.3), 0 2px 6px\\\n        \\ 2px rgba(60,64,67,.15)\\\",\\\"AsC4Mb\\\":\\\"#9aa0a6\\\",\\\"mub7Fd\\\":\\\"#f1f3f4\\\",\\\"\\\n        z2SQwf\\\":\\\"#bdc1c6\\\",\\\"ob4Y0c\\\":\\\"#e8eaed\\\",\\\"M1fk3b\\\":\\\"#dadce0\\\",\\\"gWINCf\\\"\\\n        :\\\"#9aa0a6\\\",\\\"I6R5lf\\\":\\\"#f8f9fa\\\",\\\"KCMXVb\\\":\\\"#202124\\\",\\\"vzRvgb\\\":\\\"#e8f0fe\\\"\\\n        ,\\\"HNLwz\\\":\\\"#d2e3fc\\\",\\\"uD3Lwc\\\":\\\"#d2e3fc\\\",\\\"MLAA8d\\\":\\\"0 1px 2px rgba(66,133,244,.3),\\\n        \\ 0 1px 3px 1px rgba(66,133,244,.15)\\\",\\\"TqDTGf\\\":\\\"#aecbfa\\\",\\\"m7EnTc\\\":\\\"\\\n        #8ab4f8\\\",\\\"jyEUXe\\\":\\\"#d2e3fc\\\",\\\"QyzZ8e\\\":\\\"#174ea6\\\",\\\"CFgsb\\\":\\\"#1558d6\\\"\\\n        ,\\\"lYyelb\\\":\\\"rgba(0,0,0,.54)\\\",\\\"gdL5yf\\\":\\\"rgba(0,0,0,.26)\\\",\\\"uWxHhb\\\"\\\n        :\\\"#fff\\\",\\\"tCxmde\\\":\\\"rgba(255,255,255,.30)\\\",\\\"m0RlKb\\\":false,\\\"wFGKdc\\\"\\\n        :false,\\\"klgere\\\":\\\"invert(1) hue-rotate(180deg)\\\",\\\"gHo7b\\\":\\\"#b8bbbe\\\",\\\"\\\n        VBSc8c\\\":false,\\\"oX2r2c\\\":true,\\\"WitVqe\\\":true,\\\"HIMA4e\\\":false,\\\"YjL9Ce\\\"\\\n        :false,\\\"wsRfI\\\":false,\\\"UZoA2e\\\":false,\\\"q49bvd\\\":false,\\\"m2hzy\\\":false,\\\"\\\n        jBwTk\\\":\\\"#3c4043\\\",\\\"eOLVib\\\":10,\\\"fTZUNc\\\":false,\\\"YrTYaf\\\":true,\\\"WvdhF\\\"\\\n        :false,\\\"Rojixc\\\":\\\"#aecbfa\\\",\\\"QOuvIc\\\":\\\"#1a73e8\\\",\\\"hhsybf\\\":false,\\\"Zxl9ce\\\"\\\n        :false,\\\"Ydluub\\\":\\\"#f5f5f5\\\",\\\"GV48mf\\\":\\\"rgba(0,0,0,.87)\\\",\\\"OL2x3c\\\":false,\\\"\\\n        Zun3Ef\\\":false,\\\"SOm4o\\\":false,\\\"l4Npee\\\":false,\\\"tyCgpc\\\":\\\"#fff\\\",\\\"H7aRye\\\"\\\n        :\\\"0px 5px 26px 0px rgba(0, 0, 0, 0.22), 0px 20px 28px 0px rgba(0, 0, 0, 0.30)\\\"\\\n        ,\\\"U6xP0\\\":\\\"#4285f4\\\",\\\"A5tF3b\\\":false,\\\"j0DpSe\\\":true,\\\"GUwCvc\\\":false,\\\"\\\n        ilb27b\\\":\\\"#4285f4\\\",\\\"MpqkGd\\\":\\\"#202124\\\",\\\"NXDvtf\\\":false,\\\"Lxmjn\\\":true,\\\"\\\n        kCmuvf\\\":false,\\\"FydCC\\\":true,\\\"EgTnfe\\\":true,\\\"kAUP3b\\\":false,\\\"hgWJ8c\\\"\\\n        :false,\\\"TxsTcf\\\":\\\"#000\\\",\\\"v4iQCe\\\":\\\"#4285f4\\\",\\\"OfqeOe\\\":\\\"#4285f4\\\",\\\"\\\n        zRpUk\\\":\\\"#4285f4\\\",\\\"QbZklb\\\":\\\"#e8f0fe\\\",\\\"Fcb4A\\\":\\\"#1a73e8\\\",\\\"VRtZRe\\\"\\\n        :\\\"#1558d6\\\",\\\"OmYlge\\\":\\\"#34a853\\\",\\\"y8HGgf\\\":\\\"#1e8e3e\\\",\\\"QDXUyc\\\":\\\"#188038\\\"\\\n        ,\\\"JQWqub\\\":\\\"#ea4335\\\",\\\"nRwuZd\\\":\\\"#d93025\\\",\\\"rzzybc\\\":\\\"#d93025\\\",\\\"rZLJJb\\\"\\\n        :\\\"#fff\\\",\\\"hcLEtc\\\":\\\"#81c995\\\",\\\"GJQmmf\\\":\\\"#34a853\\\",\\\"hETIfb\\\":\\\"#dadce0\\\"\\\n        ,\\\"NtNjtd\\\":\\\"#dadce0\\\",\\\"vCsrw\\\":\\\"#dadce0\\\",\\\"p9416c\\\":\\\"#f8f9fa\\\",\\\"toQ7tf\\\"\\\n        :\\\"#f8f9fa\\\",\\\"xgY1nc\\\":\\\"#f8f9fa\\\",\\\"p1ocJb\\\":\\\"#f8f9fa\\\",\\\"FCLfBe\\\":\\\"#f8f9fa\\\"\\\n        ,\\\"MnC2zf\\\":\\\"#70757a\\\",\\\"IfdAAd\\\":\\\"#70757a\\\",\\\"fP2Yo\\\":\\\"#70757a\\\",\\\"mknyu\\\"\\\n        :\\\"#70757a\\\",\\\"PUenT\\\":\\\"#3c4043\\\",\\\"Z0DEKf\\\":\\\"#202124\\\",\\\"oHHKwf\\\":\\\"#202124\\\"\\\n        ,\\\"xNPzic\\\":\\\"#fff\\\",\\\"KkPbyc\\\":\\\"#fbbc04\\\",\\\"uezre\\\":\\\"#fbbc04\\\",\\\"SkGiZd\\\"\\\n        :\\\"#f29900\\\",\\\"OxPRr\\\":\\\"#f1f3f4\\\",\\\"uiKEV\\\":\\\"#202124\\\",\\\"HMhiYd\\\":\\\"#202124\\\"\\\n        ,\\\"Co7tHc\\\":true,\\\"qcvoqe\\\":false,\\\"BPltf\\\":\\\"#f1f3f4\\\",\\\"kcrUme\\\":14,\\\"bKebqb\\\"\\\n        :\\\"#202124\\\",\\\"qeEJkc\\\":40,\\\"zHsZtb\\\":\\\"#3c4043\\\",\\\"urZDtf\\\":\\\"#202124\\\",\\\"\\\n        zeWvtf\\\":false,\\\"qdoinb\\\":\\\"#70757a\\\",\\\"QU9sdc\\\":\\\"#202124\\\",\\\"a4qLne\\\":\\\"\\\n        #ea4335\\\",\\\"RifN2d\\\":\\\"#000\\\",\\\"Fpi7Rc\\\":\\\"arial,sans-serif-medium,sans-serif\\\"\\\n        ,\\\"a2ykac\\\":\\\"arial,sans-serif\\\",\\\"ME4NMc\\\":\\\"#000\\\",\\\"BpPAcd\\\":\\\"#dadce0\\\"\\\n        ,\\\"N0wyZ\\\":\\\"#000\\\",\\\"jxZxne\\\":\\\"#70757a\\\",\\\"CQvMbe\\\":\\\"#1a73e8\\\",\\\"fRkoq\\\"\\\n        :true,\\\"c4qycc\\\":true,\\\"WkjuOe\\\":false,\\\"uJ8Xid\\\":false,\\\"cWwp7b\\\":false,\\\"\\\n        h6eQZc\\\":false,\\\"b0Jode\\\":false,\\\"mo8CW\\\":true,\\\"fd9gQc\\\":false,\\\"MomrM\\\"\\\n        :false,\\\"Vb9YJ\\\":true,\\\"OQZvxe\\\":\\\"0 2px 10px 0 rgba(0,0,0,0.2)\\\",\\\"fI0P7e\\\"\\\n        :true,\\\"Asoj0e\\\":false,\\\"AP8pqf\\\":\\\"#dadce0\\\",\\\"sBpVac\\\":\\\"rgba(0,0,0,.26)\\\"\\\n        ,\\\"JcUGee\\\":\\\"#70757a\\\",\\\"PngPbb\\\":\\\"#202124\\\",\\\"ENmP1c\\\":\\\"rgba(204,204,204,.15)\\\"\\\n        ,\\\"I69zkb\\\":\\\"rgba(204,204,204,.25)\\\",\\\"ib0wve\\\":\\\"rgba(112,117,122,.20)\\\"\\\n        ,\\\"a8Umdd\\\":\\\"rgba(112,117,122,.40)\\\",\\\"LVoecd\\\":\\\"rgba(0,0,0,.16)\\\",\\\"yHlFbb\\\"\\\n        :\\\"rgba(0,0,0,.40)\\\",\\\"seVajd\\\":\\\"rgba(0,0,0,.12)\\\",\\\"qj36Ef\\\":\\\"#323232\\\"\\\n        ,\\\"esUgv\\\":\\\"#fff\\\",\\\"KVmtZc\\\":\\\"rgba(255,255,255,.30)\\\",\\\"MoAfyf\\\":\\\"#fff\\\"\\\n        ,\\\"oyB9kf\\\":\\\"#202124\\\",\\\"bXvyY\\\":\\\"#fff\\\",\\\"ALMSwe\\\":\\\"Roboto,RobotoDraft,Helvetica,Arial,sans-serif\\\"\\\n        ,\\\"Sgnmlc\\\":\\\"14px\\\",\\\"qkXvHd\\\":\\\"500\\\",\\\"SezQgf\\\":\\\"500\\\",\\\"EJG4vf\\\":\\\"pointer\\\"\\\n        ,\\\"WyvaRd\\\":\\\"0 1px 1px rgba(0,0,0,.16)\\\",\\\"ROAn0e\\\":\\\"0 2px 2px 0 rgba(0,0,0,.14),0\\\n        \\ 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12)\\\",\\\"rgHLF\\\":true,\\\"\\\n        NQ4wzb\\\":false,\\\"TLsp9d\\\":false,\\\"eSe9wb\\\":\\\"#000\\\",\\\"RxFwtc\\\":\\\"0 4px 16px\\\n        \\ rgba(0,0,0,0.2)\\\",\\\"aM8S7c\\\":\\\"#666\\\",\\\"Tae7A\\\":true,\\\"c5h25\\\":true,\\\"MCowFd\\\"\\\n        :false,\\\"LACYrf\\\":false,\\\"uZLNF\\\":true,\\\"wku5sd\\\":false,\\\"bDOvJc\\\":false,\\\"\\\n        HCImye\\\":false,\\\"ZMIIMe\\\":true,\\\"B0husb\\\":true,\\\"o28sBd\\\":false,\\\"n4eEIc\\\"\\\n        :true,\\\"tqmosb\\\":\\\"#fff\\\",\\\"HjM8R\\\":\\\"#fff\\\",\\\"ruFjfe\\\":false,\\\"FqP1Fc\\\":\\\"\\\n        #000\\\",\\\"SATNMc\\\":\\\"1px solid #dadce0\\\",\\\"V0Bluc\\\":\\\"none\\\",\\\"X1bUEc\\\":\\\"\\\n        arial,sans-serif-medium,sans-serif\\\",\\\"QZheGe\\\":\\\"Google Sans,arial,sans-serif-medium,sans-serif\\\"\\\n        ,\\\"LIYDac\\\":\\\"arial,sans-serif\\\",\\\"mNmrAb\\\":\\\"#ebebeb\\\",\\\"x0VCkc\\\":\\\"1px solid\\\n        \\ #dadce0\\\",\\\"Rvxsx\\\":\\\"1px solid #dadce0\\\",\\\"qmcJmd\\\":6,\\\"JuqxTb\\\":\\\"#202124\\\"\\\n        ,\\\"E6Gkjd\\\":\\\"0 2px 10px 0 rgba(0,0,0,0.2)\\\",\\\"MClBOe\\\":\\\"rgba(0,0,0,0.1)\\\"\\\n        ,\\\"V6eh7c\\\":16,\\\"ZxI7Af\\\":\\\"#D8D7DC\\\",\\\"sKPNrc\\\":\\\"#e6e6e6\\\",\\\"AgJzQ\\\":\\\"\\\n        rgba(255,255,255,0)\\\",\\\"FagChc\\\":\\\"#f1f3f4\\\",\\\"tCGJz\\\":\\\"#D8D7DC\\\",\\\"oqx7yb\\\"\\\n        :\\\"#70757a\\\",\\\"khoEPb\\\":\\\"#1a0dab\\\",\\\"SfSmD\\\":\\\"#dadce0\\\",\\\"auaxA\\\":\\\"#202124\\\"\\\n        ,\\\"v44rSc\\\":\\\"#70757a\\\",\\\"YkyDVb\\\":false,\\\"s6k9tc\\\":true,\\\"tdC6kd\\\":true,\\\"\\\n        fhD9ff\\\":false,\\\"avBLic\\\":false,\\\"UjGOq\\\":false,\\\"sib8M\\\":true,\\\"PGBLg\\\":false,\\\"\\\n        pWkoAb\\\":false,\\\"IUj4Ye\\\":true,\\\"KYi16e\\\":false,\\\"wUvFOb\\\":false,\\\"a1lsHe\\\"\\\n        :true,\\\"z8cfje\\\":false,\\\"kBxgab\\\":true,\\\"aMqn0b\\\":true,\\\"lHLMtb\\\":false,\\\"\\\n        Erzlz\\\":false,\\\"KQw3Q\\\":false,\\\"OQFPef\\\":false,\\\"m19P4e\\\":false,\\\"P6Ur2b\\\"\\\n        :\\\"#1a73e8\\\",\\\"uhXPIc\\\":\\\"#8ab4f8\\\",\\\"e127Sb\\\":\\\"#1c3aa9\\\",\\\"ezFdNd\\\":\\\"#0f9d58\\\"\\\n        ,\\\"Wja4f\\\":\\\"#87ceac\\\",\\\"jjajId\\\":\\\"#9e9e9e\\\",\\\"d1ULv\\\":\\\"rgba(0,0,0,.26)\\\"\\\n        ,\\\"lQ1kYd\\\":\\\"#bdbdbd\\\",\\\"fAus6\\\":\\\"#000\\\",\\\"NNBneb\\\":\\\"#5f6368\\\",\\\"MDi8Rd\\\"\\\n        :\\\"#dadce0\\\",\\\"BoJtxf\\\":false,\\\"ZTuJNc\\\":false,\\\"XgWQKd\\\":true,\\\"fjc61\\\":false,\\\"\\\n        y1HZEd\\\":false,\\\"D8A8he\\\":false,\\\"nMRhJe\\\":true,\\\"JyBo2c\\\":false,\\\"xDKXr\\\"\\\n        :false,\\\"FYBlgf\\\":false,\\\"FELoce\\\":false,\\\"HpkQdc\\\":false,\\\"wwQMXe\\\":false,\\\"\\\n        bcz7kc\\\":false,\\\"VXIo7d\\\":\\\"8px\\\",\\\"EiEfXb\\\":\\\"#dadce0\\\",\\\"IFkMhd\\\":false,\\\"\\\n        lsK6rd\\\":true,\\\"TSsjXd\\\":true,\\\"w2btAe\\\":\\\"%.@.\\\\\\\"\\\\\\\",\\\\\\\"\\\\\\\",\\\\\\\"0\\\\\\\"\\\n        ,null,null,null,1]\\\",\\\"pxO4Zd\\\":\\\"0\\\",\\\"mXOY5d\\\":\\\"%.@.null,1,1,null,[null,757,1440]]\\\"\\\n        ,\\\"SsQ4x\\\":\\\"fiRsZQKlRm7AeAysGAgLGA\\\",\\\"IYFWl\\\":\\\"%.@.\\\\\\\"#b8bbbe\\\\\\\"]\\\",\\\"\\\n        Ht1O2b\\\":\\\"%.@.0]\\\",\\\"d6J1ld\\\":\\\"%.@.0]\\\",\\\"Oo3dKf\\\":\\\"%.@.\\\\\\\"0px 5px 26px\\\n        \\ 0px rgba(0,0,0,0.22),0px 20px 28px 0px rgba(0,0,0,0.3)\\\\\\\",\\\\\\\"#fff\\\\\\\"\\\n        ]\\\",\\\"uUBnEb\\\":\\\"%.@.\\\\\\\"#ebeced\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#dadce0\\\\\\\",\\\\\\\"#fff\\\\\\\n        \\\",\\\\\\\"#202124\\\\\\\",\\\\\\\"#dadce0\\\\\\\",\\\\\\\"#dadce0\\\\\\\",\\\\\\\"#9aa0a6\\\\\\\",\\\\\\\"#fff\\\\\\\n        \\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#202124\\\\\\\",\\\\\\\"#3c4043\\\\\\\",\\\\\\\"#ea4335\\\\\\\",\\\\\\\"#34a853\\\\\\\n        \\\",\\\\\\\"#202124\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#202124\\\\\\\",\\\\\\\"#202124\\\\\\\n        \\\",\\\\\\\"#3c4043\\\\\\\"]\\\",\\\"nfxEDe\\\":\\\"%.@.[],0,null,0,0]\\\",\\\"YPqjbf\\\":\\\"%.@.\\\\\\\n        \\\"rgba(0,0,0,0.9)\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"0 0 2px 0 rgba(0,0,0,0.12),0 2px 2px\\\n        \\ 0 rgba(0,0,0,0.12)\\\\\\\",\\\\\\\"1px solid #dadce0\\\\\\\",\\\\\\\"#70757a\\\\\\\"]\\\",\\\"GWsdKe\\\"\\\n        :\\\"id-ID\\\",\\\"frJqAd\\\":\\\"%.@.\\\\\\\"13px\\\\\\\",\\\\\\\"16px\\\\\\\",\\\\\\\"11px\\\\\\\",13,16,11,\\\\\\\n        \\\"8px\\\\\\\",8,20]\\\",\\\"N1ycab\\\":\\\"id_ID\\\",\\\"AB5Xwb\\\":\\\"%.@.\\\\\\\"10px\\\\\\\",10,\\\\\\\n        \\\"16px\\\\\\\",16,\\\\\\\"18px\\\\\\\"]\\\",\\\"Z8HLFf\\\":\\\"%.@.\\\\\\\"14px\\\\\\\",14]\\\",\\\"ymaOI\\\"\\\n        :\\\"%.@.40,32,14]\\\",\\\"fNpQmb\\\":\\\"%.@.\\\\\\\"Roboto-Bold,HelveticaNeue-Bold,HelveticaNeue,sans-serif-bold,Arial,sans-serif\\\\\\\n        \\\"]\\\",\\\"aMI2mb\\\":\\\"%.@.\\\\\\\"0 2px 10px 0 rgba(0,0,0,0.2)\\\\\\\"]\\\",\\\"BZUDzc\\\"\\\n        :\\\"%.@.0,\\\\\\\"14px\\\\\\\",\\\\\\\"500\\\\\\\",\\\\\\\"500\\\\\\\",\\\\\\\"0 1px 1px rgba(0,0,0,.16)\\\\\\\n        \\\",\\\\\\\"pointer\\\\\\\",\\\\\\\"#000\\\\\\\",\\\\\\\"rgba(0,0,0,.26)\\\\\\\",\\\\\\\"#70757a\\\\\\\",\\\\\\\n        \\\"#202124\\\\\\\",\\\\\\\"rgba(204,204,204,.15)\\\\\\\",\\\\\\\"rgba(204,204,204,.25)\\\\\\\"\\\n        ,\\\\\\\"rgba(112,117,122,.20)\\\\\\\",\\\\\\\"rgba(112,117,122,.40)\\\\\\\",\\\\\\\"#34a853\\\\\\\n        \\\",\\\\\\\"#4285f4\\\\\\\",\\\\\\\"#1558d6\\\\\\\",\\\\\\\"#ea4335\\\\\\\",\\\\\\\"#fbbc04\\\\\\\",\\\\\\\"#f8f9fa\\\\\\\n        \\\",\\\\\\\"#f8f9fa\\\\\\\",\\\\\\\"#202124\\\\\\\",\\\\\\\"#34a853\\\\\\\",\\\\\\\"rgba(0,0,0,.12)\\\\\\\"\\\n        ,null,\\\\\\\"#fff\\\\\\\",\\\\\\\"rgba(255,255,255,.30)\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#202124\\\\\\\n        \\\",\\\\\\\"#fff\\\\\\\",null,1]\\\",\\\"v7Qvdc\\\":\\\"%.@.\\\\\\\"20px\\\\\\\",\\\\\\\"500\\\\\\\",\\\\\\\"400\\\\\\\n        \\\",\\\\\\\"13px\\\\\\\",\\\\\\\"15px\\\\\\\",\\\\\\\"15px\\\\\\\",\\\\\\\"Roboto,RobotoDraft,Helvetica,Arial,sans-serif\\\\\\\n        \\\",\\\\\\\"24px\\\\\\\",\\\\\\\"400\\\\\\\",\\\\\\\"32px\\\\\\\",\\\\\\\"24px\\\\\\\"]\\\",\\\"MgUcDb\\\":\\\"ID\\\"\\\n        ,\\\"SIsrTd\\\":false,\\\"fyLpDc\\\":\\\"\\\",\\\"ZxtPCd\\\":\\\"%.@.null,null,null,null,\\\\\\\"\\\n        20\\\\\\\",\\\\\\\"20\\\\\\\",\\\\\\\"18\\\\\\\",\\\\\\\"40px\\\\\\\",\\\\\\\"36px\\\\\\\",\\\\\\\"36px\\\\\\\",null,null,null,null,null,null,null,null,null,null,\\\\\\\n        \\\"#fff\\\\\\\",null,null,null,\\\\\\\"#e8f0fe\\\\\\\",null,\\\\\\\"#e8f0fe\\\\\\\",null,null,\\\\\\\n        \\\"16px\\\\\\\",\\\\\\\"12px\\\\\\\",\\\\\\\"8px\\\\\\\",\\\\\\\"4px\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#e8f0fe\\\\\\\n        \\\",\\\\\\\"#1967d2\\\\\\\",\\\\\\\"transparent\\\\\\\",\\\\\\\"#1a0dab\\\\\\\",\\\\\\\"#dadce0\\\\\\\",\\\\\\\"\\\n        999rem\\\\\\\",\\\\\\\"8px\\\\\\\",\\\\\\\"#1967d2\\\\\\\",\\\\\\\"transparent\\\\\\\",\\\\\\\"#4d5156\\\\\\\"\\\n        ,\\\\\\\"#dadce0\\\\\\\",\\\\\\\"#1967d2\\\\\\\",null,null,\\\\\\\"#dadce0\\\\\\\",\\\\\\\"999rem\\\\\\\"\\\n        ,\\\\\\\"Google Sans,arial,sans-serif-medium,sans-serif\\\\\\\",\\\\\\\"20px\\\\\\\",\\\\\\\"\\\n        14px\\\\\\\",\\\\\\\"500\\\\\\\",\\\\\\\"#f1f3f4\\\\\\\",\\\\\\\"#202124\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#dadce0\\\\\\\n        \\\",\\\\\\\"#3c4043\\\\\\\",\\\\\\\"4px\\\\\\\",\\\\\\\"#1967d2\\\\\\\",\\\\\\\"#1967d2\\\\\\\",null,null,\\\\\\\n        \\\"#4285f4\\\\\\\",null,null,null,null,null,\\\\\\\"#185abc\\\\\\\",null,\\\\\\\"#4285f4\\\\\\\"\\\n        ,\\\\\\\"2px\\\\\\\",null,null,\\\\\\\"rgba(138,180,248,0.24)\\\\\\\",null,null,null,\\\\\\\"\\\n        #1967d2\\\\\\\",\\\\\\\"34px\\\\\\\",null,\\\\\\\"7px\\\\\\\",\\\\\\\"1px\\\\\\\",null,null,null,null,null,null,\\\\\\\n        \\\"rgba(26,115,232,0.08)\\\\\\\",\\\\\\\"rgba(26,115,232,0.08)\\\\\\\",null,\\\\\\\"#1967d2\\\\\\\n        \\\",\\\\\\\"#4d5156\\\\\\\"]\\\",\\\"bqityb\\\":\\\"%.@.null,null,null,null,null,null,null,null,null,\\\\\\\n        \\\"#1967d2\\\\\\\",\\\\\\\"#1967d2\\\\\\\"]\\\",\\\"NyzCwe\\\":\\\"%.@.\\\\\\\"#70757a\\\\\\\",\\\\\\\"#70757a\\\\\\\n        \\\",\\\\\\\"#70757a\\\\\\\",\\\\\\\"#70757a\\\\\\\",\\\\\\\"#4d5156\\\\\\\",\\\\\\\"#70757a\\\\\\\",\\\\\\\"8px\\\\\\\n        \\\",\\\\\\\"100%\\\\\\\",\\\\\\\"12px\\\\\\\",\\\\\\\"0px\\\\\\\",\\\\\\\"8px\\\\\\\",\\\\\\\"8px\\\\\\\",\\\\\\\"4px\\\\\\\n        \\\",\\\\\\\"100%\\\\\\\",\\\\\\\"6px\\\\\\\",\\\\\\\"8px\\\\\\\",\\\\\\\"0px\\\\\\\",\\\\\\\"16px\\\\\\\"]\\\",\\\"spz2q\\\"\\\n        :\\\"%.@.\\\\\\\"#fff\\\\\\\",\\\\\\\"0px\\\\\\\",\\\\\\\"0px\\\\\\\",\\\\\\\"0px\\\\\\\",\\\\\\\"0px 0px 30px\\\\\\\n        \\\"]\\\",\\\"TmSkMb\\\":\\\"%.@.\\\\\\\"center\\\\\\\",\\\\\\\"flex\\\\\\\",\\\\\\\"row wrap\\\\\\\",\\\\\\\"1\\\n        \\ 1 100%\\\\\\\",\\\\\\\"1 1 calc(50% - 4px)\\\\\\\",\\\\\\\"space-between\\\\\\\",\\\\\\\"0px 0px\\\n        \\ 30px\\\\\\\"]\\\",\\\"lDqiof\\\":\\\"%.@.\\\\\\\"#202124\\\\\\\",\\\\\\\"#4d5156\\\\\\\",\\\\\\\"#1a73e8\\\\\\\n        \\\",null,\\\\\\\"#70757a\\\\\\\",\\\\\\\"#1a0dab\\\\\\\",\\\\\\\"#681da8\\\\\\\",null,null,\\\\\\\"#fff\\\\\\\n        \\\",\\\\\\\"#4285f4\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#e8f0fe\\\\\\\",\\\\\\\"#1967d2\\\\\\\",\\\\\\\"#f1f3f4\\\\\\\n        \\\",\\\\\\\"#202124\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#3c4043\\\\\\\",\\\\\\\"#202124\\\\\\\",\\\\\\\"#fff\\\\\\\n        \\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#188038\\\\\\\",\\\\\\\"#d93025\\\\\\\",\\\\\\\"#e37400\\\\\\\n        \\\",\\\\\\\"#dadce0\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"rgba(0,0,0,0.6)\\\\\\\",\\\\\\\"#202124\\\\\\\",\\\\\\\n        \\\"#dadce0\\\\\\\",\\\\\\\"#d2e3fc\\\\\\\",null,\\\\\\\"#1a73e8\\\\\\\",\\\\\\\"#70757a\\\\\\\",null,\\\\\\\n        \\\"transparent\\\\\\\",\\\\\\\"#ecedef\\\\\\\",\\\\\\\"rgba(0,0,0,0.03)\\\\\\\",\\\\\\\"#4d5156\\\\\\\"\\\n        ,\\\\\\\"#202124\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#1a0dab\\\\\\\",\\\\\\\"#70757a\\\\\\\",\\\\\\\"#fff\\\\\\\"\\\n        ,\\\\\\\"#202124\\\\\\\",null,\\\\\\\"#70757a\\\\\\\",\\\\\\\"#ea4335\\\\\\\",\\\\\\\"#34a853\\\\\\\",\\\\\\\"\\\n        #4285f4\\\\\\\",\\\\\\\"#fbbc04\\\\\\\",\\\\\\\"#fbbc04\\\\\\\",\\\\\\\"#dadce0\\\\\\\",\\\\\\\"#f8f9fa\\\\\\\"\\\n        ,\\\\\\\"#fff\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#dadce0\\\\\\\",\\\\\\\"#1a73e8\\\\\\\"]\\\",\\\"sCU50d\\\":\\\"\\\n        %.@.null,\\\\\\\"none\\\\\\\",null,\\\\\\\"0px 1px 3px rgba(60,64,67,0.08)\\\\\\\",null,\\\\\\\n        \\\"0px 2px 6px rgba(60,64,67,0.16)\\\\\\\",null,\\\\\\\"0px 4px 12px rgba(60,64,67,0.24)\\\\\\\n        \\\",null,null,\\\\\\\"1px solid #dadce0\\\\\\\",\\\\\\\"none\\\\\\\",\\\\\\\"none\\\\\\\",\\\\\\\"none\\\\\\\n        \\\"]\\\",\\\"w9Zicc\\\":\\\"%.@.\\\\\\\"#f1f3f4\\\\\\\",\\\\\\\"26px\\\\\\\",\\\\\\\"#e2eeff\\\\\\\",\\\\\\\"#0060f0\\\\\\\n        \\\",\\\\\\\"#e2eeff\\\\\\\",\\\\\\\"1px\\\\\\\",\\\\\\\"#ecedef\\\\\\\",\\\\\\\"1px\\\\\\\"]\\\",\\\"IkSsrf\\\":\\\"\\\n        %.@.\\\\\\\"Google Sans,arial,sans-serif\\\\\\\",\\\\\\\"Google Sans,arial,sans-serif-medium,sans-serif\\\\\\\n        \\\",\\\\\\\"arial,sans-serif\\\\\\\",\\\\\\\"arial,sans-serif-medium,sans-serif\\\\\\\",\\\\\\\"\\\n        arial,sans-serif-light,sans-serif\\\\\\\"]\\\",\\\"OItNqf\\\":\\\"%.@.\\\\\\\"1px\\\\\\\",\\\\\\\"\\\n        20\\\\\\\",\\\\\\\"20px\\\\\\\",\\\\\\\"14px\\\\\\\"]\\\",\\\"JMyuH\\\":\\\"%.@.null,null,\\\\\\\"column\\\\\\\n        \\\",\\\\\\\"row\\\\\\\",\\\\\\\"flex\\\\\\\",\\\\\\\"1 1 100%\\\\\\\",\\\\\\\"1 1 calc(50% - 4px)\\\\\\\",\\\\\\\n        \\\"wrap\\\\\\\",null,null,\\\\\\\"1\\\\\\\",\\\\\\\"0\\\\\\\",null,null,\\\\\\\"100%\\\\\\\",\\\\\\\"center\\\\\\\n        \\\",\\\\\\\"flex-start\\\\\\\",\\\\\\\"left\\\\\\\",\\\\\\\"space-between\\\\\\\",\\\\\\\"start\\\\\\\",null,\\\\\\\n        \\\"36px\\\\\\\",\\\\\\\"20px\\\\\\\",\\\\\\\"12\\\\\\\",\\\\\\\"7\\\\\\\",\\\\\\\"20\\\\\\\"]\\\",\\\"e2zoW\\\":\\\"%.@.\\\\\\\n        \\\"16px\\\\\\\",\\\\\\\"12px\\\\\\\",\\\\\\\"0px\\\\\\\",\\\\\\\"8px\\\\\\\",\\\\\\\"4px\\\\\\\",\\\\\\\"2px\\\\\\\",\\\\\\\n        \\\"20px\\\\\\\",\\\\\\\"24px\\\\\\\",\\\\\\\"48px\\\\\\\",\\\\\\\"20px 20px\\\\\\\"]\\\",\\\"W1Bte\\\":\\\"%.@.\\\\\\\n        \\\"cubic-bezier(0.1,1,0.2,1)\\\\\\\",\\\\\\\"cubic-bezier(0.8,0,1,0.8)\\\\\\\",\\\\\\\"cubic-bezier(0.2,0.6,0.2,1)\\\\\\\n        \\\",\\\\\\\"cubic-bezier(0.4,0,1,0.8)\\\\\\\",\\\\\\\"300\\\\\\\",\\\\\\\"100ms\\\\\\\",\\\\\\\"200ms\\\\\\\n        \\\",\\\\\\\"250ms\\\\\\\",\\\\\\\"cubic-bezier(0.4,0,0.2,1)\\\\\\\"]\\\",\\\"u9mep\\\":\\\"%.@.\\\\\\\"\\\n        #1a0dab\\\\\\\",\\\\\\\"#1a0dab\\\\\\\"]\\\",\\\"mrqaQb\\\":\\\"%.@.14,6,68]\\\",\\\"k7Tqye\\\":\\\"%.@.null,null,null,null,null,null,null,\\\\\\\n        \\\"16px\\\\\\\",\\\\\\\"12px\\\\\\\",\\\\\\\"8px\\\\\\\",\\\\\\\"20px\\\\\\\",\\\\\\\"4px\\\\\\\",\\\\\\\"999rem\\\\\\\"\\\n        ,\\\\\\\"0px\\\\\\\",\\\\\\\"2px\\\\\\\"]\\\",\\\"jfSEkd\\\":\\\"%.@.\\\\\\\"#4285f4\\\\\\\",\\\\\\\"#e8f0fe\\\\\\\n        \\\",\\\\\\\"#1967d2\\\\\\\",\\\\\\\"#185abc\\\\\\\",\\\\\\\"#d2e3fc\\\\\\\",\\\\\\\"#d2e3fc\\\\\\\",\\\\\\\"rgba(26,115,232,0.24)\\\\\\\n        \\\",\\\\\\\"rgba(60,64,67,0.38)\\\\\\\",\\\\\\\"#dadce0\\\\\\\",\\\\\\\"rgba(218,220,224,0.38)\\\\\\\n        \\\",\\\\\\\"#f8f9fa\\\\\\\",\\\\\\\"#4285f4\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"rgba(32,33,36,0.16)\\\\\\\"\\\n        ,\\\\\\\"rgba(32,33,36,0.16)\\\\\\\",\\\\\\\"rgba(32,33,36,0.32)\\\\\\\",\\\\\\\"#f1f3f4\\\\\\\",\\\\\\\n        \\\"#202124\\\\\\\",\\\\\\\"rgba(32,33,36,0.08)\\\\\\\",\\\\\\\"rgba(32,33,36,0.08)\\\\\\\",\\\\\\\"\\\n        rgba(32,33,36,0.24)\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#1a73e8\\\\\\\",\\\\\\\"#1967d2\\\\\\\",\\\\\\\"\\\n        rgba(26,115,232,0.08)\\\\\\\",\\\\\\\"rgba(26,115,232,0.08)\\\\\\\",\\\\\\\"rgba(26,115,232,0.24)\\\\\\\n        \\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#70757a\\\\\\\",\\\\\\\"#4d5156\\\\\\\",\\\\\\\"rgba(60,64,67,0.08)\\\\\\\"\\\n        ,\\\\\\\"rgba(60,64,67,0.08)\\\\\\\",\\\\\\\"rgba(60,64,67,0.24)\\\\\\\",\\\\\\\"2px\\\\\\\",\\\\\\\"\\\n        2px\\\\\\\"]\\\",\\\"GVtPm\\\":\\\"%.@.\\\\\\\"#fff\\\\\\\",\\\\\\\"8px\\\\\\\",\\\\\\\"0 0 0 1px #dadce0\\\\\\\n        \\\",\\\\\\\"1px solid #dadce0\\\\\\\"]\\\",\\\"MexNte\\\":\\\"%.@.\\\\\\\"700\\\\\\\",\\\\\\\"400\\\\\\\",\\\\\\\n        \\\"underline\\\\\\\",\\\\\\\"none\\\\\\\",\\\\\\\"capitalize\\\\\\\",\\\\\\\"none\\\\\\\",\\\\\\\"uppercase\\\\\\\n        \\\",\\\\\\\"none\\\\\\\",\\\\\\\"500\\\\\\\",\\\\\\\"lowercase\\\\\\\",\\\\\\\"italic\\\\\\\",null,null,\\\\\\\"\\\n        -1px\\\\\\\",\\\\\\\"0.3px\\\\\\\"]\\\",\\\"Aahcnf\\\":\\\"%.@.\\\\\\\"28px\\\\\\\",\\\\\\\"36px\\\\\\\",\\\\\\\"\\\n        500\\\\\\\",\\\\\\\"Google Sans,arial,sans-serif\\\\\\\",null,\\\\\\\"arial,sans-serif\\\\\\\"\\\n        ,\\\\\\\"14px\\\\\\\",\\\\\\\"400\\\\\\\",\\\\\\\"22px\\\\\\\",null,\\\\\\\"18px\\\\\\\",\\\\\\\"24px\\\\\\\",\\\\\\\"\\\n        400\\\\\\\",\\\\\\\"Google Sans,arial,sans-serif\\\\\\\",null,\\\\\\\"Google Sans,arial,sans-serif\\\\\\\n        \\\",\\\\\\\"56px\\\\\\\",\\\\\\\"48px\\\\\\\",\\\\\\\"0\\\\\\\",null,\\\\\\\"400\\\\\\\",\\\\\\\"Google Sans,arial,sans-serif\\\\\\\n        \\\",\\\\\\\"36px\\\\\\\",\\\\\\\"400\\\\\\\",\\\\\\\"40px\\\\\\\",null,\\\\\\\"Google Sans,arial,sans-serif\\\\\\\n        \\\",\\\\\\\"36px\\\\\\\",\\\\\\\"28px\\\\\\\",null,\\\\\\\"400\\\\\\\",null,\\\\\\\"arial,sans-serif\\\\\\\"\\\n        ,\\\\\\\"24px\\\\\\\",\\\\\\\"16px\\\\\\\",null,\\\\\\\"400\\\\\\\",\\\\\\\"arial,sans-serif\\\\\\\",\\\\\\\"\\\n        16px\\\\\\\",\\\\\\\"12px\\\\\\\",null,\\\\\\\"400\\\\\\\",\\\\\\\"arial,sans-serif\\\\\\\",\\\\\\\"22px\\\\\\\n        \\\",\\\\\\\"16px\\\\\\\",null,\\\\\\\"400\\\\\\\",\\\\\\\"arial,sans-serif\\\\\\\",\\\\\\\"24px\\\\\\\",\\\\\\\"\\\n        20px\\\\\\\",null,\\\\\\\"400\\\\\\\",\\\\\\\"arial,sans-serif\\\\\\\",\\\\\\\"24px\\\\\\\",\\\\\\\"16px\\\\\\\n        \\\",null,\\\\\\\"400\\\\\\\",\\\\\\\"arial,sans-serif\\\\\\\",\\\\\\\"18px\\\\\\\",\\\\\\\"14px\\\\\\\",null,\\\\\\\n        \\\"400\\\\\\\",null,null,null,null,null,\\\\\\\"14px\\\\\\\",\\\\\\\"Google Sans,arial,sans-serif-medium,sans-serif\\\\\\\n        \\\",\\\\\\\"20px\\\\\\\",\\\\\\\"500\\\\\\\",\\\\\\\"Google Sans,arial,sans-serif\\\\\\\",\\\\\\\"26px\\\\\\\n        \\\",\\\\\\\"22px\\\\\\\",\\\\\\\"400\\\\\\\",\\\\\\\"Google Sans,arial,sans-serif\\\\\\\",\\\\\\\"24px\\\\\\\n        \\\",\\\\\\\"16px\\\\\\\",\\\\\\\"400\\\\\\\",\\\\\\\"arial,sans-serif-medium,sans-serif\\\\\\\",\\\\\\\"\\\n        12px\\\\\\\",\\\\\\\"12px\\\\\\\",\\\\\\\"Google Sans,arial,sans-serif\\\\\\\",\\\\\\\"28px\\\\\\\",\\\\\\\n        \\\"22px\\\\\\\",\\\\\\\"400\\\\\\\"]\\\",\\\"PFhmed\\\":\\\"%.@.\\\\\\\"rgba(255,255,255,0)\\\\\\\"]\\\"\\\n        ,\\\"mf1yif\\\":\\\"%.@.4]\\\",\\\"aKXqGc\\\":\\\"%.@.\\\\\\\"14px\\\\\\\",14,\\\\\\\"16px\\\\\\\",16,\\\\\\\n        \\\"0\\\\\\\",0,\\\\\\\"none\\\\\\\",652,\\\\\\\"1px solid #dadce0\\\\\\\",\\\\\\\"normal\\\\\\\",\\\\\\\"normal\\\\\\\n        \\\",\\\\\\\"#70757a\\\\\\\",\\\\\\\"12px\\\\\\\",\\\\\\\"1.34\\\\\\\",\\\\\\\"1px solid #dadce0\\\\\\\",\\\\\\\"\\\n        none\\\\\\\",\\\\\\\"0\\\\\\\",\\\\\\\"none\\\\\\\",\\\\\\\"none\\\\\\\",\\\\\\\"none\\\\\\\",\\\\\\\"none\\\\\\\",\\\\\\\"\\\n        6px\\\\\\\",\\\\\\\"652px\\\\\\\"]\\\",\\\"ZP0oif\\\":\\\"%.@.\\\\\\\"16px\\\\\\\",\\\\\\\"#ebedef\\\\\\\"]\\\"\\\n        ,\\\"o0P8Hf\\\":\\\"%.@.\\\\\\\"rgba(0,0,0,0.0)\\\\\\\",null,null,null,\\\\\\\"#202124\\\\\\\",\\\\\\\n        \\\"#dadce0\\\\\\\",null,null,null,\\\\\\\"#f8f9fa\\\\\\\",\\\\\\\"#000\\\\\\\",\\\\\\\"#1a73e8\\\\\\\"\\\n        ,\\\\\\\"#dadce0\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#fff\\\\\\\",null,\\\\\\\"#70757a\\\\\\\",\\\\\\\"rgba(0,0,0,0.26)\\\\\\\n        \\\",\\\\\\\"rgba(0,0,0,0.2)\\\\\\\",\\\\\\\"rgba(0,0,0,0.5)\\\\\\\",\\\\\\\"rgba(0,0,0,0.2)\\\\\\\"\\\n        ,\\\\\\\"#fff\\\\\\\",\\\\\\\"rgba(0,0,0,0.1)\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#70757a\\\\\\\",null,\\\\\\\n        \\\"#000\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#000\\\\\\\",\\\\\\\"rgba(0,0,0,0.0)\\\\\\\",\\\\\\\"rgba(255,255,255,0.5)\\\\\\\n        \\\",\\\\\\\"rgba(0,0,0,.03)\\\\\\\",\\\\\\\"rgba(0,0,0,0.3)\\\\\\\",\\\\\\\"rgba(0,0,0,0.2)\\\\\\\"\\\n        ,\\\\\\\"rgba(0,0,0,0.5)\\\\\\\",\\\\\\\"rgba(0,0,0,.07)\\\\\\\",\\\\\\\"rgba(0,0,0,.04)\\\\\\\",\\\\\\\n        \\\"rgba(0,0,0,.26)\\\\\\\",\\\\\\\"rgba(255,255,255,.54)\\\\\\\",\\\\\\\"#70757a\\\\\\\",\\\\\\\"#70757a\\\\\\\n        \\\",\\\\\\\"rgba(0,0,0,.22)\\\\\\\",\\\\\\\"rgba(0,0,0,.30)\\\\\\\",\\\\\\\"rgba(0,0,0,.06)\\\\\\\"\\\n        ,\\\\\\\"rgba(0,0,0,.25)\\\\\\\",\\\\\\\"#d2e3fc\\\\\\\",\\\\\\\"rgba(32,33,36,.5)\\\\\\\",\\\\\\\"rgba(32,33,36,.7)\\\\\\\n        \\\",\\\\\\\"rgba(255,255,255,.04)\\\\\\\",null,null,\\\\\\\"rgba(255,255,255,.8)\\\\\\\",\\\\\\\n        \\\"rgba(60,64,67,.15)\\\\\\\",\\\\\\\"rgba(0,0,0,.07)\\\\\\\",\\\\\\\"rgba(0,0,0,.16)\\\\\\\",\\\\\\\n        \\\"rgba(0,0,0,.08)\\\\\\\",\\\\\\\"rgba(0,0,0,.14)\\\\\\\",\\\\\\\"rgba(0,0,0,.12)\\\\\\\",\\\\\\\"\\\n        rgba(0,0,0,.28)\\\\\\\",\\\\\\\"rgba(0,0,0,.18)\\\\\\\",\\\\\\\"rgba(0,0,0,.24)\\\\\\\",\\\\\\\"rgba(0,0,0,.05)\\\\\\\n        \\\",\\\\\\\"rgba(0,0,0,.13)\\\\\\\",\\\\\\\"rgba(60,64,67,.3)\\\\\\\",\\\\\\\"rgba(0,0,0,.36)\\\\\\\n        \\\",\\\\\\\"rgba(0,0,0,.15)\\\\\\\",\\\\\\\"rgba(32,33,36,.28)\\\\\\\",\\\\\\\"rgba(218,220,224,.7)\\\\\\\n        \\\",\\\\\\\"#dadce0\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"#1a73e8\\\\\\\",\\\\\\\"#000\\\\\\\"\\\n        ,\\\\\\\"rgba(0,0,0,.0)\\\\\\\",\\\\\\\"#202124\\\\\\\",\\\\\\\"rgba(0,0,0,.8)\\\\\\\",\\\\\\\"rgba(26,115,232,0)\\\\\\\n        \\\",\\\\\\\"rgba(26,115,232,.7)\\\\\\\",\\\\\\\"rgba(66,133,244,.22)\\\\\\\",\\\\\\\"rgba(32,33,36,.7)\\\\\\\n        \\\",\\\\\\\"rgba(0,0,0,.8)\\\\\\\",\\\\\\\"rgba(255,255,255,.54)\\\\\\\",\\\\\\\"rgba(255,255,255,.87)\\\\\\\n        \\\",\\\\\\\"rgba(60,64,67,.38)\\\\\\\",\\\\\\\"rgba(0,0,0,.8)\\\\\\\",\\\\\\\"rgba(255,255,255,.54)\\\\\\\n        \\\",\\\\\\\"rgba(255,255,255,.87)\\\\\\\",\\\\\\\"rgba(60,64,67,.38)\\\\\\\",\\\\\\\"rgba(255,255,255,.3)\\\\\\\n        \\\",\\\\\\\"rgba(0,0,0,0.54)\\\\\\\",\\\\\\\"rgba(0,0,0,0.8)\\\\\\\",\\\\\\\"rgba(248,249,250,0.85)\\\\\\\n        \\\",\\\\\\\"#dadce0\\\\\\\",\\\\\\\"#ea4335\\\\\\\",\\\\\\\"#34a853\\\\\\\",\\\\\\\"#3c4043\\\\\\\",\\\\\\\"#f8f9fa\\\\\\\n        \\\",\\\\\\\"#3c4043\\\\\\\",\\\\\\\"#202124\\\\\\\",{\\\\\\\"100\\\\\\\":\\\\\\\"#f8f9fa\\\\\\\",\\\\\\\"101\\\\\\\"\\\n        :\\\\\\\"#dadce0\\\\\\\",\\\\\\\"102\\\\\\\":\\\\\\\"#3c4043\\\\\\\",\\\\\\\"103\\\\\\\":\\\\\\\"#202124\\\\\\\",\\\\\\\n        \\\"104\\\\\\\":\\\\\\\"#f8f9fa\\\\\\\",\\\\\\\"105\\\\\\\":\\\\\\\"#dadce0\\\\\\\",\\\\\\\"106\\\\\\\":\\\\\\\"#70757a\\\\\\\n        \\\",\\\\\\\"107\\\\\\\":\\\\\\\"#3c4043\\\\\\\",\\\\\\\"108\\\\\\\":\\\\\\\"#f8f9fa\\\\\\\",\\\\\\\"109\\\\\\\":\\\\\\\"\\\n        #3c4043\\\\\\\",\\\\\\\"110\\\\\\\":\\\\\\\"#202124\\\\\\\",\\\\\\\"111\\\\\\\":\\\\\\\"#f8f9fa\\\\\\\",\\\\\\\"112\\\\\\\n        \\\":\\\\\\\"#dadce0\\\\\\\",\\\\\\\"113\\\\\\\":\\\\\\\"#e8f0fe\\\\\\\",\\\\\\\"114\\\\\\\":\\\\\\\"#4285f4\\\\\\\"\\\n        ,\\\\\\\"115\\\\\\\":\\\\\\\"#e8f0fe\\\\\\\",\\\\\\\"116\\\\\\\":\\\\\\\"#d2e3fc\\\\\\\",\\\\\\\"117\\\\\\\":\\\\\\\"\\\n        #4285f4\\\\\\\",\\\\\\\"118\\\\\\\":\\\\\\\"#1a73e8\\\\\\\",\\\\\\\"119\\\\\\\":\\\\\\\"#e8f0fe\\\\\\\",\\\\\\\"120\\\\\\\n        \\\":\\\\\\\"#d2e3fc\\\\\\\",\\\\\\\"121\\\\\\\":\\\\\\\"#4285f4\\\\\\\",\\\\\\\"122\\\\\\\":\\\\\\\"#1a73e8\\\\\\\"\\\n        ,\\\\\\\"123\\\\\\\":\\\\\\\"#d2e3fc\\\\\\\",\\\\\\\"124\\\\\\\":\\\\\\\"#4285f4\\\\\\\",\\\\\\\"125\\\\\\\":\\\\\\\"\\\n        #1a73e8\\\\\\\",\\\\\\\"126\\\\\\\":\\\\\\\"#e8f0fe\\\\\\\",\\\\\\\"127\\\\\\\":\\\\\\\"#d2e3fc\\\\\\\",\\\\\\\"128\\\\\\\n        \\\":\\\\\\\"#4285f4\\\\\\\",\\\\\\\"129\\\\\\\":\\\\\\\"#1a73e8\\\\\\\",\\\\\\\"130\\\\\\\":\\\\\\\"#fce8e6\\\\\\\"\\\n        ,\\\\\\\"131\\\\\\\":\\\\\\\"#fad2cf\\\\\\\",\\\\\\\"132\\\\\\\":\\\\\\\"#f28b82\\\\\\\",\\\\\\\"133\\\\\\\":\\\\\\\"\\\n        #ee675c\\\\\\\",\\\\\\\"134\\\\\\\":\\\\\\\"#d93025\\\\\\\",\\\\\\\"135\\\\\\\":\\\\\\\"#c5221f\\\\\\\",\\\\\\\"136\\\\\\\n        \\\":\\\\\\\"#a50e0e\\\\\\\",\\\\\\\"137\\\\\\\":\\\\\\\"#ea4335\\\\\\\",\\\\\\\"138\\\\\\\":\\\\\\\"#d93025\\\\\\\"\\\n        ,\\\\\\\"139\\\\\\\":\\\\\\\"#ea4335\\\\\\\",\\\\\\\"140\\\\\\\":\\\\\\\"#d93025\\\\\\\",\\\\\\\"141\\\\\\\":\\\\\\\"\\\n        #c5221f\\\\\\\",\\\\\\\"142\\\\\\\":\\\\\\\"#b31412\\\\\\\",\\\\\\\"143\\\\\\\":\\\\\\\"#a50e0e\\\\\\\",\\\\\\\"144\\\\\\\n        \\\":\\\\\\\"#d93025\\\\\\\",\\\\\\\"145\\\\\\\":\\\\\\\"#f28b82\\\\\\\",\\\\\\\"146\\\\\\\":\\\\\\\"#ee675c\\\\\\\"\\\n        ,\\\\\\\"147\\\\\\\":\\\\\\\"#ea4335\\\\\\\",\\\\\\\"148\\\\\\\":\\\\\\\"#c5221f\\\\\\\",\\\\\\\"149\\\\\\\":\\\\\\\"\\\n        #a50e0e\\\\\\\",\\\\\\\"150\\\\\\\":\\\\\\\"#fef7e0\\\\\\\",\\\\\\\"151\\\\\\\":\\\\\\\"#feefc3\\\\\\\",\\\\\\\"152\\\\\\\n        \\\":\\\\\\\"#fde293\\\\\\\",\\\\\\\"153\\\\\\\":\\\\\\\"#fdd663\\\\\\\",\\\\\\\"154\\\\\\\":\\\\\\\"#fcc934\\\\\\\"\\\n        ,\\\\\\\"155\\\\\\\":\\\\\\\"#fbbc04\\\\\\\",\\\\\\\"156\\\\\\\":\\\\\\\"#f9ab00\\\\\\\",\\\\\\\"157\\\\\\\":\\\\\\\"\\\n        #f29900\\\\\\\",\\\\\\\"158\\\\\\\":\\\\\\\"#ea8600\\\\\\\",\\\\\\\"159\\\\\\\":\\\\\\\"#e37400\\\\\\\",\\\\\\\"160\\\\\\\n        \\\":\\\\\\\"#fbbc04\\\\\\\",\\\\\\\"161\\\\\\\":\\\\\\\"#fbbc04\\\\\\\",\\\\\\\"162\\\\\\\":\\\\\\\"#f29900\\\\\\\"\\\n        ,\\\\\\\"163\\\\\\\":\\\\\\\"#fdd663\\\\\\\",\\\\\\\"164\\\\\\\":\\\\\\\"#fbbc04\\\\\\\",\\\\\\\"165\\\\\\\":\\\\\\\"\\\n        #fcc934\\\\\\\",\\\\\\\"166\\\\\\\":\\\\\\\"#fbbc04\\\\\\\",\\\\\\\"167\\\\\\\":\\\\\\\"#f9ab00\\\\\\\",\\\\\\\"168\\\\\\\n        \\\":\\\\\\\"#f29900\\\\\\\",\\\\\\\"169\\\\\\\":\\\\\\\"#ea8600\\\\\\\",\\\\\\\"170\\\\\\\":\\\\\\\"#e37400\\\\\\\"\\\n        ,\\\\\\\"171\\\\\\\":\\\\\\\"#e6f4ea\\\\\\\",\\\\\\\"172\\\\\\\":\\\\\\\"#ceead6\\\\\\\",\\\\\\\"173\\\\\\\":\\\\\\\"\\\n        #a8dab5\\\\\\\",\\\\\\\"174\\\\\\\":\\\\\\\"#81c995\\\\\\\",\\\\\\\"175\\\\\\\":\\\\\\\"#5bb974\\\\\\\",\\\\\\\"176\\\\\\\n        \\\":\\\\\\\"#1e8e3e\\\\\\\",\\\\\\\"177\\\\\\\":\\\\\\\"#188038\\\\\\\",\\\\\\\"178\\\\\\\":\\\\\\\"#34a853\\\\\\\"\\\n        ,\\\\\\\"179\\\\\\\":\\\\\\\"#1e8e3e\\\\\\\",\\\\\\\"180\\\\\\\":\\\\\\\"#188038\\\\\\\",\\\\\\\"181\\\\\\\":\\\\\\\"\\\n        #34a853\\\\\\\",\\\\\\\"182\\\\\\\":\\\\\\\"#1e8e3e\\\\\\\",\\\\\\\"183\\\\\\\":\\\\\\\"#ceead6\\\\\\\",\\\\\\\"184\\\\\\\n        \\\":\\\\\\\"#a8dab5\\\\\\\",\\\\\\\"185\\\\\\\":\\\\\\\"#34a853\\\\\\\",\\\\\\\"186\\\\\\\":\\\\\\\"#81c995\\\\\\\"\\\n        ,\\\\\\\"187\\\\\\\":\\\\\\\"#34a853\\\\\\\",\\\\\\\"188\\\\\\\":\\\\\\\"#1e8e3e\\\\\\\",\\\\\\\"189\\\\\\\":\\\\\\\"\\\n        #188038\\\\\\\",\\\\\\\"190\\\\\\\":\\\\\\\"#137333\\\\\\\",\\\\\\\"191\\\\\\\":\\\\\\\"#0d652d\\\\\\\",\\\\\\\"192\\\\\\\n        \\\":\\\\\\\"rgba(0,0,0,.1)\\\\\\\",\\\\\\\"193\\\\\\\":\\\\\\\"rgba(0,0,0,.2)\\\\\\\",\\\\\\\"194\\\\\\\":\\\\\\\n        \\\"rgba(60,64,67,.1)\\\\\\\",\\\\\\\"195\\\\\\\":\\\\\\\"rgba(60,64,67,.06)\\\\\\\",\\\\\\\"196\\\\\\\"\\\n        :\\\\\\\"rgba(255,255,255,0)\\\\\\\",\\\\\\\"197\\\\\\\":\\\\\\\"rgba(0,0,0,.12)\\\\\\\",\\\\\\\"198\\\\\\\n        \\\":\\\\\\\"rgba(32,33,36,0)\\\\\\\",\\\\\\\"199\\\\\\\":\\\\\\\"rgba(32,33,36,.1)\\\\\\\",\\\\\\\"200\\\\\\\n        \\\":\\\\\\\"rgba(0,0,0,.12)\\\\\\\",\\\\\\\"201\\\\\\\":\\\\\\\"rgba(0,0,0,.5)\\\\\\\",\\\\\\\"202\\\\\\\"\\\n        :\\\\\\\"rgba(0,0,0,.54)\\\\\\\",\\\\\\\"203\\\\\\\":\\\\\\\"#000\\\\\\\",\\\\\\\"204\\\\\\\":\\\\\\\"rgba(255,255,255,.5)\\\\\\\n        \\\",\\\\\\\"205\\\\\\\":\\\\\\\"#1558d6\\\\\\\",\\\\\\\"206\\\\\\\":\\\\\\\"rgba(0,0,0,.24)\\\\\\\",\\\\\\\"207\\\\\\\n        \\\":\\\\\\\"rgba(0,0,0,.24)\\\\\\\",\\\\\\\"208\\\\\\\":\\\\\\\"#f8f9fa\\\\\\\",\\\\\\\"209\\\\\\\":\\\\\\\"rgba(255,255,255,.6)\\\\\\\n        \\\",\\\\\\\"210\\\\\\\":\\\\\\\"#1e8e3e\\\\\\\",\\\\\\\"211\\\\\\\":\\\\\\\"rgba(0,0,0,.02)\\\\\\\",\\\\\\\"212\\\\\\\n        \\\":\\\\\\\"#000\\\\\\\",\\\\\\\"213\\\\\\\":\\\\\\\"rgba(0,0,0,.16)\\\\\\\",\\\\\\\"214\\\\\\\":\\\\\\\"rgba(0,0,0,.7)\\\\\\\n        \\\",\\\\\\\"215\\\\\\\":\\\\\\\"#1a73e8\\\\\\\",\\\\\\\"216\\\\\\\":\\\\\\\"#d93025\\\\\\\",\\\\\\\"217\\\\\\\":\\\\\\\"\\\n        #4285f4\\\\\\\",\\\\\\\"218\\\\\\\":\\\\\\\"rgba(0,0,0,.15)\\\\\\\",\\\\\\\"219\\\\\\\":\\\\\\\"rgba(0,0,0,.05)\\\\\\\n        \\\",\\\\\\\"220\\\\\\\":\\\\\\\"#70757a\\\\\\\",\\\\\\\"221\\\\\\\":\\\\\\\"#dadce0\\\\\\\",\\\\\\\"222\\\\\\\":\\\\\\\"\\\n        #188038\\\\\\\",\\\\\\\"223\\\\\\\":\\\\\\\"rgba(0,0,0,.6)\\\\\\\",\\\\\\\"224\\\\\\\":\\\\\\\"#34a853\\\\\\\"\\\n        ,\\\\\\\"225\\\\\\\":\\\\\\\"rgba(255,255,255,.3)\\\\\\\",\\\\\\\"226\\\\\\\":\\\\\\\"rgba(0,0,0,.05)\\\\\\\n        \\\",\\\\\\\"227\\\\\\\":\\\\\\\"rgba(0,0,0,.05)\\\\\\\",\\\\\\\"228\\\\\\\":\\\\\\\"rgba(32,33,36,.9)\\\\\\\n        \\\",\\\\\\\"229\\\\\\\":\\\\\\\"rgba(255,255,255,.6)\\\\\\\",\\\\\\\"230\\\\\\\":\\\\\\\"rgba(0,0,0,.08)\\\\\\\n        \\\",\\\\\\\"231\\\\\\\":\\\\\\\"rgba(255,255,255,.8)\\\\\\\",\\\\\\\"232\\\\\\\":\\\\\\\"rgba(0,0,0,.05)\\\\\\\n        \\\",\\\\\\\"233\\\\\\\":\\\\\\\"#4285f4\\\\\\\",\\\\\\\"234\\\\\\\":\\\\\\\"rgba(0,0,0,.16)\\\\\\\",\\\\\\\"235\\\\\\\n        \\\":\\\\\\\"#fff\\\\\\\",\\\\\\\"236\\\\\\\":\\\\\\\"rgba(0,0,0,.87)\\\\\\\",\\\\\\\"238\\\\\\\":\\\\\\\"#fdd663\\\\\\\n        \\\",\\\\\\\"239\\\\\\\":\\\\\\\"#fdd663\\\\\\\",\\\\\\\"240\\\\\\\":\\\\\\\"#fff\\\\\\\",\\\\\\\"241\\\\\\\":\\\\\\\"rgba(255,255,255,.5)\\\\\\\n        \\\",\\\\\\\"242\\\\\\\":\\\\\\\"#f8f9fa\\\\\\\",\\\\\\\"243\\\\\\\":\\\\\\\"#fdd663\\\\\\\",\\\\\\\"244\\\\\\\":\\\\\\\"\\\n        rgba(255,255,255,.54)\\\\\\\",\\\\\\\"245\\\\\\\":\\\\\\\"rgba(0,0,0,.5)\\\\\\\",\\\\\\\"246\\\\\\\":\\\\\\\n        \\\"rgba(0,0,0,.26)\\\\\\\",\\\\\\\"247\\\\\\\":\\\\\\\"rgba(0,0,0,.26)\\\\\\\",\\\\\\\"248\\\\\\\":\\\\\\\"\\\n        rgba(0,0,0,.38)\\\\\\\",\\\\\\\"249\\\\\\\":\\\\\\\"rgba(0,0,0,.03)\\\\\\\",\\\\\\\"250\\\\\\\":\\\\\\\"#4285f4\\\\\\\n        \\\",\\\\\\\"251\\\\\\\":\\\\\\\"rgba(60,64,67,.12)\\\\\\\",\\\\\\\"252\\\\\\\":\\\\\\\"rgba(255,255,255,0)\\\\\\\n        \\\",\\\\\\\"253\\\\\\\":\\\\\\\"rgba(0,0,0,0)\\\\\\\",\\\\\\\"254\\\\\\\":\\\\\\\"#3c4043\\\\\\\",\\\\\\\"255\\\\\\\n        \\\":\\\\\\\"#d2e3fc\\\\\\\",\\\\\\\"256\\\\\\\":\\\\\\\"#3c4043\\\\\\\",\\\\\\\"257\\\\\\\":\\\\\\\"#d2e3fc\\\\\\\"\\\n        ,\\\\\\\"258\\\\\\\":\\\\\\\"#d2e3fc\\\\\\\",\\\\\\\"259\\\\\\\":\\\\\\\"#4285f4\\\\\\\",\\\\\\\"260\\\\\\\":\\\\\\\"\\\n        #202124\\\\\\\",\\\\\\\"261\\\\\\\":\\\\\\\"rgba(0,0,0,.16)\\\\\\\",\\\\\\\"262\\\\\\\":\\\\\\\"rgba(255,255,255,.3)\\\\\\\n        \\\",\\\\\\\"263\\\\\\\":\\\\\\\"rgba(0,0,0,0)\\\\\\\",\\\\\\\"264\\\\\\\":\\\\\\\"#c5221f\\\\\\\",\\\\\\\"265\\\\\\\n        \\\":\\\\\\\"#dadce0\\\\\\\",\\\\\\\"266\\\\\\\":\\\\\\\"#ea4335\\\\\\\",\\\\\\\"267\\\\\\\":\\\\\\\"#34a853\\\\\\\"\\\n        ,\\\\\\\"268\\\\\\\":\\\\\\\"rgba(60,64,67,.15)\\\\\\\",\\\\\\\"269\\\\\\\":\\\\\\\"rgba(19,115,51,.15)\\\\\\\n        \\\",\\\\\\\"270\\\\\\\":\\\\\\\"rgba(0,0,0,.15)\\\\\\\"}]\\\",\\\"WiLzZe\\\":\\\"%.@.\\\\\\\"#202124\\\\\\\"\\\n        ,\\\\\\\"#70757a\\\\\\\",\\\\\\\"#4d5156\\\\\\\",\\\\\\\"#5f6368\\\\\\\",\\\\\\\"#fff\\\\\\\",\\\\\\\"rgba(255,255,255,.70)\\\\\\\n        \\\",28,24,26,20,16,-2,0,-4,2,0,0,24,20,20,14,12]\\\",\\\"AYkLRe\\\":\\\"%.@.\\\\\\\"20px\\\\\\\n        \\\",20,\\\\\\\"14px\\\\\\\",14,\\\\\\\"\\\\\\\\\\\\\\\"rgba(0, 0, 0, .87)\\\\\\\\\\\\\\\"\\\\\\\"]\\\",\\\"rNyuJc\\\"\\\n        :\\\"\\\",\\\"LU5fGb\\\":false,\\\"gXkHoe\\\":\\\"105250506097979753968\\\",\\\"hevonc\\\":\\\"\\\n        %.@.1]\\\",\\\"xcljyb\\\":\\\"%.@.\\\\\\\"8px\\\\\\\",8,\\\\\\\"Roboto-Medium,HelveticaNeue-Medium,Helvetica\\\n        \\ Neue,sans-serif-medium,Arial,sans-serif\\\\\\\"]\\\"};})();window.jsl=window.jsl||{};window.jsl.dh=function(a,b,f){try{var\\\n        \\ g=document.getElementById(a);if(g)g.innerHTML=b,f&&f();else{var c={id:a,script:String(!!f),milestone:String(google.jslm||0)};google.jsla&&(c.async=google.jsla);var\\\n        \\ h=a.indexOf(\\\"_\\\"),d=0<h?a.substring(0,h):\\\"\\\",k=document.createElement(\\\"\\\n        div\\\");k.innerHTML=b;var e=k.children[0];if(e&&(c.tag=e.tagName,c[\\\"class\\\"\\\n        ]=String(e.className||null),c.name=String(e.getAttribute(\\\"jsname\\\")),d)){a=[];var\\\n        \\ l=document.querySelectorAll('[id^=\\\"'+d+'_\\\"]');for(b=0;b<l.length;++b)a.push(l[b].id);c.ids=a.join(\\\"\\\n        ,\\\")}google.ml(Error(d?\\\"Missing ID with prefix \\\"+d:\\\"Missing ID\\\"),!1,c)}}catch(m){google.ml(m,!0,{\\\"\\\n        jsl.dh\\\":!0})}};(function(){var x=true;google.jslm=x?2:1;})();google.x(null,\\\n        \\ function(){(function(){(function(){google.csct={};google.csct.ps='AOvVaw0kwOMCvNlrXtRkvSfp_FnR\\\\\\\n        x26ust\\\\x3d1673164227810097';})();})();(function(){(function(){google.csct.rw=true;})();})();(function(){(function(){google.csct.rl=true;})();})();(function(){window.jsl=window.jsl||{};window.jsl.dh=window.jsl.dh||function(i,c,d){try{var\\\n        \\ e=document.getElementById(i);if(e){e.innerHTML=c;if(d){d();}}else{if(window.jsl.el){window.jsl.el(new\\\n        \\ Error('Missing ID.'),{'id':i});}}}catch(e){if(window.jsl.el){window.jsl.el(new\\\n        \\ Error('jsl.dh'));}}};})();(function(){window.jsl.dh('_QyS5Y_SSL5HO5OUPp_mggAY_1','\\\\\\\n        x3cstyle\\\\x3e.gb_6a:not(.gb_Md){font:13px/27px Roboto,Arial,sans-serif;z-index:986}@-moz-keyframes\\\n        \\ gb__a{0%{opacity:0}50%{opacity:1}}@keyframes gb__a{0%{opacity:0}50%{opacity:1}}a.gb_3{border:none;color:#4285f4;cursor:default;font-weight:bold;outline:none;position:relative;text-align:center;text-decoration:none;text-transform:uppercase;white-space:nowrap;-moz-user-select:none}a.gb_3:hover:after,a.gb_3:focus:after{background-color:rgba(0,0,0,.12);content:\\\\\\\n        x22\\\\x22;height:100%;left:0;position:absolute;top:0;width:100%}a.gb_3:hover,a.gb_3:focus{text-decoration:none}a.gb_3:active{background-color:rgba(153,153,153,.4);text-decoration:none}a.gb_4{background-color:#4285f4;color:#fff}a.gb_4:active{background-color:#0043b2}.gb_5{-moz-box-shadow:0\\\n        \\ 1px 1px rgba(0,0,0,.16);box-shadow:0 1px 1px rgba(0,0,0,.16)}.gb_3,.gb_4,.gb_6,.gb_7{display:inline-block;line-height:28px;padding:0\\\n        \\ 12px;-moz-border-radius:2px;border-radius:2px}.gb_6{background:#f8f8f8;border:1px\\\n        \\ solid #c6c6c6}.gb_7{background:#f8f8f8}.gb_6,#gb a.gb_6.gb_6,.gb_7{color:#666;cursor:default;text-decoration:none}#gb\\\n        \\ a.gb_7.gb_7{cursor:default;text-decoration:none}.gb_7{border:1px solid #4285f4;font-weight:bold;outline:none;background:#4285f4;background:-moz-linear-gradient(top,#4387fd,#4683ea);background:linear-gradient(top,#4387fd,#4683ea);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr\\\\\\\n        x3d#4387fd,endColorstr\\\\x3d#4683ea,GradientType\\\\x3d0)}#gb a.gb_7.gb_7{color:#fff}.gb_7:hover{-moz-box-shadow:0\\\n        \\ 1px 0 rgba(0,0,0,.15);box-shadow:0 1px 0 rgba(0,0,0,.15)}.gb_7:active{-moz-box-shadow:inset\\\n        \\ 0 2px 0 rgba(0,0,0,.15);box-shadow:inset 0 2px 0 rgba(0,0,0,.15);background:#3c78dc;background:-moz-linear-gradient(top,#3c7ae4,#3f76d3);background:linear-gradient(top,#3c7ae4,#3f76d3);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr\\\\\\\n        x3d#3c7ae4,endColorstr\\\\x3d#3f76d3,GradientType\\\\x3d0)}#gb .gb_9{background:#fff;border:1px\\\n        \\ solid #dadce0;color:#1a73e8;display:inline-block;text-decoration:none}#gb\\\n        \\ .gb_9:hover{background:#f8fbff;border-color:#dadce0;color:#174ea6}#gb .gb_9:focus{background:#f4f8ff;color:#174ea6;outline:1px\\\n        \\ solid #174ea6}#gb .gb_9:active,#gb .gb_9:focus:active{background:#ecf3fe;color:#174ea6}#gb\\\n        \\ .gb_9.gb_aa{background:transparent;border:1px solid #5f6368;color:#8ab4f8;text-decoration:none}#gb\\\n        \\ .gb_9.gb_aa:hover{background:rgba(255,255,255,.04);color:#e8eaed}#gb .gb_9.gb_aa:focus{background:rgba(232,234,237,.12);color:#e8eaed;outline:1px\\\n        \\ solid #e8eaed}#gb .gb_9.gb_aa:active,#gb .gb_9.gb_aa:focus:active{background:rgba(232,234,237,.1);color:#e8eaed}.gb_Fa{display:none!important}.gb_Ha{visibility:hidden}.gb_ld{display:inline-block;vertical-align:middle}.gb_Df\\\n        \\ .gb_Za{bottom:-3px;right:-5px}.gb_Ef{position:relative}.gb_d{display:inline-block;outline:none;vertical-align:middle;-moz-border-radius:2px;border-radius:2px;-moz-box-sizing:border-box;box-sizing:border-box;height:40px;width:40px;color:#000;cursor:pointer;text-decoration:none}#gb#gb\\\n        \\ a.gb_d{color:#000;cursor:pointer;text-decoration:none}.gb_8a{border-color:transparent;border-bottom-color:#fff;border-style:dashed\\\n        \\ dashed solid;border-width:0 8.5px 8.5px;display:none;position:absolute;left:11.5px;top:43px;z-index:1;height:0;width:0;-moz-animation:gb__a\\\n        \\ .2s;animation:gb__a .2s}.gb_9a{border-color:transparent;border-style:dashed\\\n        \\ dashed solid;border-width:0 8.5px 8.5px;display:none;position:absolute;left:11.5px;z-index:1;height:0;width:0;-moz-animation:gb__a\\\n        \\ .2s;animation:gb__a .2s;border-bottom-color:#ccc;border-bottom-color:rgba(0,0,0,.2);top:42px}x:-o-prefocus,div.gb_9a{border-bottom-color:#ccc}.gb_I{background:#fff;border:1px\\\n        \\ solid #ccc;border-color:rgba(0,0,0,.2);color:#000;-moz-box-shadow:0 2px\\\n        \\ 10px rgba(0,0,0,.2);box-shadow:0 2px 10px rgba(0,0,0,.2);display:none;outline:none;overflow:hidden;position:absolute;right:8px;top:62px;-moz-animation:gb__a\\\n        \\ .2s;animation:gb__a .2s;-moz-border-radius:2px;border-radius:2px;-moz-user-select:text}.gb_ld.gb_qa\\\n        \\ .gb_8a,.gb_ld.gb_qa .gb_9a,.gb_ld.gb_qa .gb_I,.gb_qa.gb_I{display:block}.gb_ld.gb_qa.gb_Ff\\\n        \\ .gb_8a,.gb_ld.gb_qa.gb_Ff .gb_9a{display:none}.gb_Hf{position:absolute;right:8px;top:62px;z-index:-1}.gb_Oa\\\n        \\ .gb_8a,.gb_Oa .gb_9a,.gb_Oa .gb_I{margin-top:-10px}.gb_ld:first-child,#gbsfw:first-child+.gb_ld{padding-left:4px}.gb_ua.gb_Ve\\\n        \\ .gb_ld:first-child{padding-left:0}.gb_We{position:relative}.gb_Wc .gb_We,.gb_3d\\\n        \\ .gb_We{float:right}.gb_d{padding:8px;cursor:pointer}.gb_ua .gb_dd:not(.gb_3):focus\\\n        \\ img{background-color:rgba(0,0,0,.20);outline:none;-moz-border-radius:50%;border-radius:50%}.gb_Xe\\\n        \\ button svg,.gb_d{-moz-border-radius:50%;border-radius:50%}.gb_Xe button:focus:not(:focus-visible)\\\n        \\ svg,.gb_Xe button:hover svg,.gb_Xe button:active svg,.gb_d:focus:not(:focus-visible),.gb_d:hover,.gb_d:active,.gb_d[aria-expanded\\\\\\\n        x3dtrue]{outline:none}.gb_Fc .gb_Xe.gb_Ze button:focus-visible svg,.gb_Xe\\\n        \\ button:focus-visible svg,.gb_d:focus-visible{outline:1px solid #202124}.gb_Fc\\\n        \\ .gb_Xe button:focus-visible svg,.gb_Fc .gb_d:focus-visible{outline:1px solid\\\n        \\ #f1f3f4}@media (forced-colors:active){.gb_Fc .gb_Xe.gb_Ze button:focus-visible\\\n        \\ svg,.gb_Xe button:focus-visible svg,.gb_Fc .gb_Xe button:focus-visible svg{outline:1px\\\n        \\ solid currentcolor}}.gb_Fc .gb_Xe.gb_Ze button:focus svg,.gb_Fc .gb_Xe.gb_Ze\\\n        \\ button:focus:hover svg,.gb_Xe button:focus svg,.gb_Xe button:focus:hover\\\n        \\ svg,.gb_d:focus,.gb_d:focus:hover{background-color:rgba(60,64,67,.1)}.gb_Fc\\\n        \\ .gb_Xe.gb_Ze button:active svg,.gb_Xe button:active svg,.gb_d:active{background-color:rgba(60,64,67,.12)}.gb_Fc\\\n        \\ .gb_Xe.gb_Ze button:hover svg,.gb_Xe button:hover svg,.gb_d:hover{background-color:rgba(60,64,67,.08)}.gb_oa\\\n        \\ .gb_d.gb_Ra:hover{background-color:transparent}.gb_d[aria-expanded\\\\x3dtrue],.gb_d:hover[aria-expanded\\\\\\\n        x3dtrue]{background-color:rgba(95,99,104,.24)}.gb_d[aria-expanded\\\\x3dtrue]\\\n        \\ .gb_0e,.gb_d[aria-expanded\\\\x3dtrue] .gb_1e{fill:#5f6368;opacity:1}.gb_Fc\\\n        \\ .gb_Xe button:hover svg,.gb_Fc .gb_d:hover{background-color:rgba(232,234,237,.08)}.gb_Fc\\\n        \\ .gb_Xe button:focus svg,.gb_Fc .gb_Xe button:focus:hover svg,.gb_Fc .gb_d:focus,.gb_Fc\\\n        \\ .gb_d:focus:hover{background-color:rgba(232,234,237,.10)}.gb_Fc .gb_Xe button:active\\\n        \\ svg,.gb_Fc .gb_d:active{background-color:rgba(232,234,237,.12)}.gb_Fc .gb_d[aria-expanded\\\\\\\n        x3dtrue],.gb_Fc .gb_d:hover[aria-expanded\\\\x3dtrue]{background-color:rgba(255,255,255,.12)}.gb_Fc\\\n        \\ .gb_d[aria-expanded\\\\x3dtrue] .gb_0e,.gb_Fc .gb_d[aria-expanded\\\\x3dtrue]\\\n        \\ .gb_1e{fill:#fff;opacity:1}.gb_ld{padding:4px}.gb_ua.gb_Ve .gb_ld{padding:4px\\\n        \\ 2px}.gb_ua.gb_Ve .gb_b.gb_ld{padding-left:6px}.gb_I{z-index:991;line-height:normal}.gb_I.gb_2e{left:8px;right:auto}@media\\\n        \\ (max-width:350px){.gb_I.gb_2e{left:0}}.gb_3e .gb_I{top:56px}.gb_F .gb_d,.gb_H\\\n        \\ .gb_F .gb_d{background-position:-64px -29px}.gb_m .gb_F .gb_d{background-position:-29px\\\n        \\ -29px;opacity:1}.gb_F .gb_d,.gb_F .gb_d:hover,.gb_F .gb_d:focus{opacity:1}.gb_Nd{display:none}.gb_5c{font-family:Google\\\n        \\ Sans,Roboto,Helvetica,Arial,sans-serif;font-size:20px;font-weight:400;letter-spacing:0.25px;line-height:48px;margin-bottom:2px;opacity:1;overflow:hidden;padding-left:16px;position:relative;text-overflow:ellipsis;vertical-align:middle;top:2px;white-space:nowrap;flex:1\\\n        \\ 1 auto}.gb_5c.gb_6c{color:#3c4043}.gb_ua.gb_va .gb_5c{margin-bottom:0}.gb_7c.gb_8c\\\n        \\ .gb_5c{padding-left:4px}.gb_ua.gb_va .gb_9c{position:relative;top:-2px}.gb_ua{color:black;min-width:320px;position:relative;-moz-transition:box-shadow\\\n        \\ 250ms;transition:box-shadow 250ms}.gb_ua.gb_Oc{min-width:240px}.gb_ua.gb_Od\\\n        \\ .gb_Pd{display:none}.gb_ua.gb_Od .gb_Qd{height:56px}header.gb_ua{display:block}.gb_ua\\\n        \\ svg{fill:currentColor}.gb_Rd{position:fixed;top:0;width:100%}.gb_Sd{-moz-box-shadow:0px\\\n        \\ 4px 5px 0px rgba(0,0,0,.14),0px 1px 10px 0px rgba(0,0,0,.12),0px 2px 4px\\\n        \\ -1px rgba(0,0,0,.2);box-shadow:0px 4px 5px 0px rgba(0,0,0,.14),0px 1px 10px\\\n        \\ 0px rgba(0,0,0,.12),0px 2px 4px -1px rgba(0,0,0,.2)}.gb_Td{height:64px}.gb_Qd{box-sizing:border-box;position:relative;width:100%;display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex;justify-content:space-between;min-width:-webkit-min-content;min-width:-moz-min-content;min-width:-ms-min-content;min-width:min-content}.gb_ua:not(.gb_va)\\\n        \\ .gb_Qd{padding:8px}.gb_ua.gb_Ud .gb_Qd{flex:1 0 auto}.gb_ua .gb_Qd.gb_Vd.gb_Wd{min-width:0}.gb_ua.gb_va\\\n        \\ .gb_Qd{padding:4px;padding-left:8px;min-width:0}.gb_Pd{height:48px;vertical-align:middle;white-space:nowrap;-moz-box-align:center;align-items:center;display:-webkit-box;display:-moz-box;display:-ms-flexbox;display:-webkit-flex;display:flex;-moz-user-select:-moz-none}.gb_Zd\\\\\\\n        x3e.gb_Pd{display:table-cell;width:100%}.gb_7c{padding-right:30px;-moz-box-sizing:border-box;box-sizing:border-box;flex:1\\\n        \\ 0 auto}.gb_ua.gb_va .gb_7c{padding-right:14px}.gb_0d{flex:1 1 100%}.gb_0d\\\\\\\n        x3e:only-child{display:inline-block}.gb_1d.gb_Xc{padding-left:4px}.gb_1d.gb_2d,.gb_ua.gb_Ud\\\n        \\ .gb_1d,.gb_ua.gb_va:not(.gb_3d) .gb_1d{padding-left:0}.gb_ua.gb_va .gb_1d.gb_2d{padding-right:0}.gb_ua.gb_va\\\n        \\ .gb_1d.gb_2d .gb_oa{margin-left:10px}.gb_Xc{display:inline}.gb_ua.gb_Rc\\\n        \\ .gb_1d.gb_4d,.gb_ua.gb_3d .gb_1d.gb_4d{padding-left:2px}.gb_5c{display:inline-block}.gb_1d{box-sizing:border-box;height:48px;line-height:normal;padding:0\\\n        \\ 4px;padding-left:30px;flex:0 0 auto;justify-content:flex-end}.gb_3d{height:48px}.gb_ua.gb_3d{min-width:initial;min-width:auto}.gb_3d\\\n        \\ .gb_1d{float:right;padding-left:32px}.gb_3d .gb_1d.gb_5d{padding-left:0}.gb_6d{font-size:14px;max-width:200px;overflow:hidden;padding:0\\\n        \\ 12px;text-overflow:ellipsis;white-space:nowrap;-moz-user-select:text}.gb_7d{transition:background-color\\\n        \\ .4s}.gb_8d{color:black}.gb_Fc{color:white}.gb_ua a,.gb_Lc a{color:inherit}.gb_w{color:rgba(0,0,0,.87)}.gb_ua\\\n        \\ svg,.gb_Lc svg,.gb_7c .gb_9d,.gb_Wc .gb_9d{color:#5f6368;opacity:1}.gb_Fc\\\n        \\ svg,.gb_Lc.gb_Pc svg,.gb_Fc .gb_7c .gb_9d,.gb_Fc .gb_7c .gb_Ec,.gb_Fc .gb_7c\\\n        \\ .gb_9c,.gb_Lc.gb_Pc .gb_9d{color:rgba(255,255,255,0.87)}.gb_Fc .gb_7c .gb_Dc:not(.gb_ae){opacity:0.87}.gb_6c{color:inherit;opacity:1;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale}.gb_Fc\\\n        \\ .gb_6c,.gb_8d .gb_6c{opacity:1}.gb_be{position:relative}.gb_ce{font-family:arial,sans-serif;line-height:normal;padding-right:15px}a.gb_j,span.gb_j{color:rgba(0,0,0,.87);text-decoration:none}.gb_Fc\\\n        \\ a.gb_j,.gb_Fc span.gb_j{color:white}a.gb_j:focus{outline-offset:2px}a.gb_j:hover{text-decoration:underline}.gb_k{display:inline-block;padding-left:15px}.gb_k\\\n        \\ .gb_j{display:inline-block;line-height:24px;vertical-align:middle}.gb_de{font-family:Google\\\n        \\ Sans,Roboto,Helvetica,Arial,sans-serif;font-weight:500;font-size:14px;letter-spacing:0.25px;line-height:16px;margin-left:10px;margin-right:8px;min-width:96px;padding:9px\\\n        \\ 23px;text-align:center;vertical-align:middle;-moz-border-radius:4px;border-radius:4px;-moz-box-sizing:border-box;box-sizing:border-box}.gb_ua.gb_3d\\\n        \\ .gb_de{margin-left:8px}#gb a.gb_7.gb_7.gb_de{cursor:pointer}.gb_7.gb_de:hover{background:#1b66c9;-moz-box-shadow:0\\\n        \\ 1px 3px 1px rgba(66,64,67,.15),0 1px 2px 0 rgba(60,64,67,.3);box-shadow:0\\\n        \\ 1px 3px 1px rgba(66,64,67,.15),0 1px 2px 0 rgba(60,64,67,.3)}.gb_7.gb_de:focus,.gb_7.gb_de:hover:focus{background:#1c5fba;-moz-box-shadow:0\\\n        \\ 1px 3px 1px rgba(66,64,67,.15),0 1px 2px 0 rgba(60,64,67,.3);box-shadow:0\\\n        \\ 1px 3px 1px rgba(66,64,67,.15),0 1px 2px 0 rgba(60,64,67,.3)}.gb_7.gb_de:active{background:#1b63c1;-moz-box-shadow:0\\\n        \\ 1px 3px 1px rgba(66,64,67,.15),0 1px 2px 0 rgba(60,64,67,.3);box-shadow:0\\\n        \\ 1px 3px 1px rgba(66,64,67,.15),0 1px 2px 0 rgba(60,64,67,.3)}.gb_de{background:#1a73e8;border:1px\\\n        \\ solid transparent}.gb_ua.gb_va .gb_de{padding:9px 15px;min-width:80px}.gb_ee{text-align:left}#gb\\\n        \\ .gb_Fc a.gb_de:not(.gb_aa),#gb.gb_Fc a.gb_de{background:#fff;border-color:#dadce0;-moz-box-shadow:none;box-shadow:none;color:#1a73e8}#gb\\\n        \\ a.gb_7.gb_aa.gb_de{background:#8ab4f8;border:1px solid transparent;-moz-box-shadow:none;box-shadow:none;color:#202124}#gb\\\n        \\ .gb_Fc a.gb_de:hover:not(.gb_aa),#gb.gb_Fc a.gb_de:hover{background:#f8fbff;border-color:#cce0fc}#gb\\\n        \\ a.gb_7.gb_aa.gb_de:hover{background:#93baf9;border-color:transparent;-moz-box-shadow:0\\\n        \\ 1px 3px 1px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.3);box-shadow:0 1px 3px\\\n        \\ 1px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.3)}#gb .gb_Fc a.gb_de:focus:not(.gb_aa),#gb\\\n        \\ .gb_Fc a.gb_de:focus:hover:not(.gb_aa),#gb.gb_Fc a.gb_de:focus:not(.gb_aa),#gb.gb_Fc\\\n        \\ a.gb_de:focus:hover:not(.gb_aa){background:#f4f8ff;outline:1px solid #c9ddfc}#gb\\\n        \\ a.gb_7.gb_aa.gb_de:focus,#gb a.gb_7.gb_aa.gb_de:focus:hover{background:#a6c6fa;border-color:transparent;-moz-box-shadow:none;box-shadow:none}#gb\\\n        \\ .gb_Fc a.gb_de:active:not(.gb_aa),#gb.gb_Fc a.gb_de:active{background:#ecf3fe}#gb\\\n        \\ a.gb_7.gb_aa.gb_de:active{background:#a1c3f9;-moz-box-shadow:0 1px 2px rgba(60,64,67,.3),0\\\n        \\ 2px 6px 2px rgba(60,64,67,.15);box-shadow:0 1px 2px rgba(60,64,67,.3),0\\\n        \\ 2px 6px 2px rgba(60,64,67,.15)}.gb_oa{background-color:rgba(255,255,255,.88);border:1px\\\n        \\ solid #dadce0;box-sizing:border-box;cursor:pointer;display:inline-block;max-height:48px;overflow:hidden;outline:none;padding:0;vertical-align:middle;width:134px;-moz-border-radius:8px;border-radius:8px}.gb_oa.gb_aa{background-color:transparent;border:1px\\\n        \\ solid #5f6368}.gb_pa{display:inherit}.gb_oa.gb_aa .gb_pa{background:#fff;-moz-border-radius:4px;border-radius:4px;display:inline-block;left:8px;margin-right:5px;position:relative;padding:3px;top:-1px}.gb_oa:hover{border:1px\\\n        \\ solid #d2e3fc;background-color:rgba(248,250,255,.88)}.gb_oa.gb_aa:hover{background-color:rgba(241,243,244,.04);border:1px\\\n        \\ solid #5f6368}.gb_oa:focus-visible,.gb_oa:focus{background-color:rgba(255,255,255);outline:1px\\\n        \\ solid #202124;-moz-box-shadow:0px 1px 2px 0px rgba(60,64,67,.3),0px 1px\\\n        \\ 3px 1px rgba(60,64,67,.15);box-shadow:0px 1px 2px 0px rgba(60,64,67,.3),0px\\\n        \\ 1px 3px 1px rgba(60,64,67,.15)}.gb_oa.gb_aa:focus-visible,.gb_oa.gb_aa:focus{background-color:rgba(241,243,244,.12);outline:1px\\\n        \\ solid #f1f3f4;-moz-box-shadow:0 1px 3px 1px rgba(0,0,0,.15),0 1px 2px 0\\\n        \\ rgba(0,0,0,.3);box-shadow:0 1px 3px 1px rgba(0,0,0,.15),0 1px 2px 0 rgba(0,0,0,.3)}.gb_oa.gb_aa:active,.gb_oa.gb_qa.gb_aa:focus{background-color:rgba(241,243,244,.1);border:1px\\\n        \\ solid #5f6368}.gb_ra{display:inline-block;padding-bottom:2px;padding-left:7px;padding-top:2px;text-align:center;vertical-align:middle;line-height:32px;width:78px}.gb_oa.gb_aa\\\n        \\ .gb_ra{line-height:26px;margin-left:0;padding-bottom:0;padding-left:0;padding-top:0;width:72px}.gb_ra.gb_sa{background-color:#f1f3f4;-moz-border-radius:4px;border-radius:4px;margin-left:8px;padding-left:0}.gb_ra.gb_sa\\\n        \\ .gb_ta{vertical-align:middle}.gb_ua:not(.gb_va) .gb_oa{margin-left:10px;margin-right:4px}.gb_wa{max-height:32px;width:78px}.gb_oa.gb_aa\\\n        \\ .gb_wa{max-height:26px;width:72px}.gb_Ia{background-size:32px 32px;border:0;-moz-border-radius:50%;border-radius:50%;display:block;margin:0px;position:relative;height:32px;width:32px;z-index:0}.gb_Ja{background-color:#e8f0fe;border:1px\\\n        \\ solid rgba(32,33,36,.08);position:relative}.gb_Ja.gb_Ia{height:30px;width:30px}.gb_Ja.gb_Ia:hover,.gb_Ja.gb_Ia:active{-moz-box-shadow:none;box-shadow:none}.gb_Ka{background:#fff;border:none;-moz-border-radius:50%;border-radius:50%;bottom:2px;-moz-box-shadow:0px\\\n        \\ 1px 2px 0px rgba(60,64,67,.30),0px 1px 3px 1px rgba(60,64,67,.15);box-shadow:0px\\\n        \\ 1px 2px 0px rgba(60,64,67,.30),0px 1px 3px 1px rgba(60,64,67,.15);height:14px;margin:2px;position:absolute;right:0;width:14px}.gb_La{color:#1f71e7;font:400\\\n        \\ 22px/32px Google Sans,Roboto,Helvetica,Arial,sans-serif;text-align:center;text-transform:uppercase}@media\\\n        \\ (min-resolution:1.25dppx),(-o-min-device-pixel-ratio:5/4),(-webkit-min-device-pixel-ratio:1.25),(min-device-pixel-ratio:1.25){.gb_Ia::before,.gb_Ma::before{display:inline-block;-moz-transform:scale(.5);transform:scale(.5);-moz-transform-origin:left\\\n        \\ 0;transform-origin:left 0}.gb_o .gb_Ma::before{-moz-transform:scale(0.416666667);transform:scale(0.416666667)}}.gb_Ia:hover,.gb_Ia:focus{-moz-box-shadow:0\\\n        \\ 1px 0 rgba(0,0,0,.15);box-shadow:0 1px 0 rgba(0,0,0,.15)}.gb_Ia:active{-moz-box-shadow:inset\\\n        \\ 0 2px 0 rgba(0,0,0,.15);box-shadow:inset 0 2px 0 rgba(0,0,0,.15)}.gb_Ia:active::after{background:rgba(0,0,0,.1);-moz-border-radius:50%;border-radius:50%;content:\\\\\\\n        x22\\\\x22;display:block;height:100%}.gb_Na{cursor:pointer;line-height:40px;min-width:30px;opacity:.75;overflow:hidden;vertical-align:middle;text-overflow:ellipsis}.gb_d.gb_Na{width:auto}.gb_Na:hover,.gb_Na:focus{opacity:.85}.gb_Oa\\\n        \\ .gb_Na,.gb_Oa .gb_Pa{line-height:26px}#gb#gb.gb_Oa a.gb_Na,.gb_Oa .gb_Pa{font-size:11px;height:auto}.gb_Qa{border-top:4px\\\n        \\ solid #000;border-left:4px dashed transparent;border-right:4px dashed transparent;display:inline-block;margin-left:6px;opacity:.75;vertical-align:middle}.gb_Ra:hover\\\n        \\ .gb_Qa{opacity:.85}.gb_oa\\\\x3e.gb_b{padding:3px 3px 3px 4px}.gb_Sa.gb_Ha{color:#fff}.gb_Ia.gb_Ta{clip-path:path(\\\\\\\n        x27M16 0C24.8366 0 32 7.16344 32 16C32 16.4964 31.9774 16.9875 31.9332 17.4723C30.5166\\\n        \\ 16.5411 28.8215 16 27 16C22.0294 16 18 20.0294 18 25C18 27.4671 18.9927\\\n        \\ 29.7024 20.6004 31.3282C19.1443 31.7653 17.5996 32 16 32C7.16344 32 0 24.8366\\\n        \\ 0 16C0 7.16344 7.16344 0 16 0Z\\\\x27)}.gb_Ua{-moz-border-radius:50%;border-radius:50%;-moz-box-shadow:0px\\\n        \\ 1px 2px 0px rgba(60,64,67,.30),0px 1px 3px 1px rgba(60,64,67,.15);box-shadow:0px\\\n        \\ 1px 2px 0px rgba(60,64,67,.30),0px 1px 3px 1px rgba(60,64,67,.15);margin:2px}.gb_Va{fill:#f9ab00}.gb_aa\\\n        \\ .gb_Va{fill:#fdd663}.gb_Wa\\\\x3e.gb_Va{fill:#d93025}.gb_aa .gb_Wa\\\\x3e.gb_Va{fill:#f28b82}.gb_Wa\\\\\\\n        x3e.gb_Xa{fill:white}.gb_Xa,.gb_aa .gb_Wa\\\\x3e.gb_Xa{fill:#202124}.gb_Za{-moz-border-radius:50%;border-radius:50%;bottom:2px;height:18px;position:absolute;right:0px;width:18px}.gb_m\\\n        \\ .gb_Na,.gb_m .gb_Qa{opacity:1}#gb#gb.gb_m.gb_m a.gb_Na,#gb#gb .gb_m.gb_m\\\n        \\ a.gb_Na{color:#fff}.gb_m.gb_m .gb_Qa{border-top-color:#fff;opacity:1}.gb_H\\\n        \\ .gb_Ia:hover,.gb_m .gb_Ia:hover,.gb_H .gb_Ia:focus,.gb_m .gb_Ia:focus{-moz-box-shadow:0\\\n        \\ 1px 0 rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.2);box-shadow:0 1px 0 rgba(0,0,0,.15),0\\\n        \\ 1px 2px rgba(0,0,0,.2)}.gb_0a .gb_b,.gb_1a .gb_b{position:absolute;right:1px}.gb_b.gb_l,.gb_2a.gb_l,.gb_Ra.gb_l{flex:0\\\n        \\ 1 auto;flex:0 1 main-size}.gb_3a.gb_4a .gb_Na{width:30px!important}.gb_5a{height:40px;position:absolute;right:-5px;top:-5px;width:40px}.gb_6a\\\n        \\ .gb_5a,.gb_7a .gb_5a{right:0;top:0}.gb_b .gb_d{padding:4px}.gb_ge{display:none}sentinel{}\\\\\\\n        x3c/style\\\\x3e',function(){;this.gbar_={CONFIG:[[[0,\\\"www.gstatic.com\\\",\\\"\\\n        og.qtm.en_US.ngqips5sdmk.2019.O\\\",\\\"co.id\\\",\\\"id\\\",\\\"1\\\",1,[4,2,\\\"\\\",\\\"\\\"\\\n        ,\\\"\\\",\\\"494597754\\\",\\\"0\\\"],null,\\\"QyS5Y6WnMKOW0AbX0p6oDA\\\",null,0,\\\"og.qtm.KNTs2wOYQ9I.L.F4.O\\\"\\\n        ,\\\"AA2YrTteKD3oQpoqSz3ExBZ-dcmaoC3Uzg\\\",\\\"AA2YrTtRK2npTFEXU9W0n1BFHTt6uqyiYQ\\\"\\\n        ,\\\"\\\",2,1,200,\\\"IDN\\\",null,null,\\\"1\\\",\\\"1\\\",1],null,[1,0.1000000014901161,2,1],[1,0.001000000047497451,1],[0,0,0,null,\\\"\\\n        \\\",\\\"\\\",\\\"\\\",\\\"\\\"],[0,0,\\\"\\\",1,0,0,0,0,0,0,null,0,0,null,0,0,null,null,0,0,0,\\\"\\\n        \\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\",null,0,0,0,0,0,null,null,null,\\\"rgba(32,33,36,1)\\\"\\\n        ,\\\"rgba(255,255,255,1)\\\",0,0,1,null,null,1,0,0],null,null,[\\\"1\\\",\\\"gci_91f30755d6a6b787dcc2a4062e6e9824.js\\\"\\\n        ,\\\"googleapis.client:gapi.iframes\\\",\\\"\\\",\\\"id\\\"],null,null,null,null,[\\\"m;/_/scs/abc-static/_/js/k=gapi.gapi.en.WEPncdil2Uw.O/d=1/rs=AHpOoo-eOecLLtOXEl3I3kIuMsKXRkDMmA/m=__features__\\\"\\\n        ,\\\"https://apis.google.com\\\",\\\"\\\",\\\"\\\",\\\"\\\",\\\"\\\",null,1,\\\"es_plusone_gc_20221206.0_p0\\\"\\\n        ,\\\"id\\\",null,0],[0.009999999776482582,\\\"co.id\\\",\\\"1\\\",[null,\\\"\\\",\\\"0\\\",null,1,5184000,null,null,\\\"\\\n        \\\",null,null,null,null,null,0,null,0,0,1,0,0,0,null,null,0,0,null,0,0,0,0,0],null,null,null,0,null,null,[\\\"\\\n        5061451\\\",\\\"google\\\\\\\\.(com|ru|ca|by|kz|com\\\\\\\\.mx|com\\\\\\\\.tr)$\\\",1]],[1,1,null,40400,1,\\\"\\\n        IDN\\\",\\\"id\\\",\\\"494597754.0\\\",8,0.009999999776482582,0,0,null,null,null,null,\\\"\\\n        3700949,3701054\\\",null,null,null,\\\"QyS5Y6WnMKOW0AbX0p6oDA\\\",0,1,0,null,2,5,\\\"\\\n        cf\\\",72,0,0,1,0,0],[[null,null,null,\\\"https://www.gstatic.com/og/_/js/k=og.qtm.en_US.ngqips5sdmk.2019.O/rt=j/m=qabr,q_dnp,qcwid,qapid/exm=qaaw,qadd,qaid,qein,qhaw,qhbr,qhch,qhga,qhid,qhin,qhpr/d=1/ed=1/rs=AA2YrTteKD3oQpoqSz3ExBZ-dcmaoC3Uzg\\\"\\\n        ],[null,null,null,\\\"https://www.gstatic.com/og/_/ss/k=og.qtm.KNTs2wOYQ9I.L.F4.O/m=qcwid/excm=qaaw,qadd,qaid,qein,qhaw,qhbr,qhch,qhga,qhid,qhin,qhpr/d=1/ed=1/ct=zgms/rs=AA2YrTtRK2npTFEXU9W0n1BFHTt6uqyiYQ\\\"\\\n        ]],null,null,null,[[[null,null,[null,null,null,\\\"https://ogs.google.ru/widget/app/so\\\"\\\n        ],0,448,328,57,4,1,0,0,63,64,8000,\\\"https://www.google.co.id/intl/id/about/products?tab=wh\\\"\\\n        ,67,1,69,null,1,70,\\\"Terjadi error saat memuat kumpulan aplikasi. Harap coba\\\n        \\ lagi dalam beberapa menit, atau buka halaman %1$sProduk Google%2$s.\\\",3,0,0,74,0,null,null,null,null,null,null,null,\\\"\\\n        /widget/app/so\\\"]],0,[null,null,null,\\\"https://www.gstatic.com/og/_/js/k=og.qtm.en_US.ngqips5sdmk.2019.O/rt=j/m=qdsh/d=1/ed=1/rs=AA2YrTteKD3oQpoqSz3ExBZ-dcmaoC3Uzg\\\"\\\n        ],\\\"1\\\",\\\"1\\\",1,0,null,\\\"id\\\",0,null,0,0,0]]],};this.gbar_=this.gbar_||{};(function(_){var\\\n        \\ window=this;\\ntry{\\n/*\\n\\n Copyright The Closure Library Authors.\\n SPDX-License-Identifier:\\\n        \\ Apache-2.0\\n*/\\nvar ja,za,Aa,Ba,Ga,Ia,Ka,La,Ra,Ua,Qa,Va,Ya,Za;_.aa=function(a,b){if(Error.captureStackTrace)Error.captureStackTrace(this,_.aa);else{const\\\n        \\ c=Error().stack;c&&(this.stack=c)}a&&(this.message=String(a));void 0!==b&&(this.cause=b)};_.ba=function(){var\\\n        \\ a=_.m.navigator;return a&&(a=a.userAgent)?a:\\\"\\\"};_.n=function(a){return-1!=_.ba().indexOf(a)};_.ca=function(){return\\\n        \\ _.n(\\\"Opera\\\")};_.da=function(){return _.n(\\\"Trident\\\")||_.n(\\\"MSIE\\\")};_.ea=function(){return\\\n        \\ _.n(\\\"Firefox\\\")||_.n(\\\"FxiOS\\\")};\\n_.ha=function(){return _.n(\\\"Safari\\\"\\\n        )&&!(_.fa()||_.n(\\\"Coast\\\")||_.ca()||_.n(\\\"Edge\\\")||_.n(\\\"Edg/\\\")||_.n(\\\"\\\n        OPR\\\")||_.ea()||_.n(\\\"Silk\\\")||_.n(\\\"Android\\\"))};_.fa=function(){return(_.n(\\\"\\\n        Chrome\\\")||_.n(\\\"CriOS\\\"))&&!_.n(\\\"Edge\\\")||_.n(\\\"Silk\\\")};_.ia=function(){return\\\n        \\ _.n(\\\"Android\\\")&&!(_.fa()||_.ea()||_.ca()||_.n(\\\"Silk\\\"))};ja=function(){return\\\n        \\ _.n(\\\"iPhone\\\")&&!_.n(\\\"iPod\\\")&&!_.n(\\\"iPad\\\")};_.la=function(){return\\\n        \\ ja()||_.n(\\\"iPad\\\")||_.n(\\\"iPod\\\")};\\n_.ma=function(a){const b=a.length;if(0<b){const\\\n        \\ c=Array(b);for(let d=0;d<b;d++)c[d]=a[d];return c}return[]};_.na=function(){return-1!=_.ba().toLowerCase().indexOf(\\\"\\\n        webkit\\\")&&!_.n(\\\"Edge\\\")};_.pa=function(a){return oa&&null!=a&&a instanceof\\\n        \\ Uint8Array};_.ra=function(a,b){if(qa)return a[qa]|=b;if(void 0!==a.Zb)return\\\n        \\ a.Zb|=b;Object.defineProperties(a,{Zb:{value:b,configurable:!0,writable:!0,enumerable:!1}});return\\\n        \\ b};_.sa=function(a,b){qa?a[qa]&&(a[qa]&=~b):void 0!==a.Zb&&(a.Zb&=~b)};\\n\\\n        _.ta=function(a){let b;qa?b=a[qa]:b=a.Zb;return null==b?0:b};_.ua=function(a,b){qa?a[qa]=b:void\\\n        \\ 0!==a.Zb?a.Zb=b:Object.defineProperties(a,{Zb:{value:b,configurable:!0,writable:!0,enumerable:!1}})};_.va=function(a){_.ra(a,1);return\\\n        \\ a};_.wa=function(a){return!!(_.ta(a)&2)};_.xa=function(a){_.ra(a,16);return\\\n        \\ a};_.ya=function(a,b){_.ua(b,(a|0)&-51)};za=function(a,b){_.ua(b,(a|18)&-41)};Aa=function(a){return\\\n        \\ null!==a&&\\\"object\\\"===typeof a&&!Array.isArray(a)&&a.constructor===Object};\\n\\\n        Ba=function(a){var b=a.length;(b=b?a[b-1]:void 0)&&Aa(b)?b.g=1:a.push({g:1})};_.Da=function(a,b){_.Ca=b;a=new\\\n        \\ a(b);_.Ca=void 0;return a};Ga=function(a){switch(typeof a){case \\\"number\\\"\\\n        :return isFinite(a)?a:String(a);case \\\"object\\\":if(a)if(Array.isArray(a)){if(0!==(_.ta(a)&128))return\\\n        \\ a=Array.prototype.slice.call(a),Ba(a),a}else{if(_.pa(a))return _.Ea(a);if(a\\\n        \\ instanceof _.Fa){const b=a.Ea;return null==b?\\\"\\\":\\\"string\\\"===typeof b?b:a.Ea=_.Ea(b)}}}return\\\n        \\ a};\\nIa=function(a,b,c,d){if(null!=a){if(Array.isArray(a))a=_.Ha(a,b,c,void\\\n        \\ 0!==d);else if(Aa(a)){const e={};for(let f in a)e[f]=Ia(a[f],b,c,d);a=e}else\\\n        \\ a=b(a,d);return a}};_.Ha=function(a,b,c,d){const e=_.ta(a);d=d?!!(e&16):void\\\n        \\ 0;a=Array.prototype.slice.call(a);for(let f=0;f<a.length;f++)a[f]=Ia(a[f],b,c,d);c(e,a);return\\\n        \\ a};Ka=function(a){return a.Wd===_.Ja?a.toJSON():Ga(a)};La=function(a,b){a&128&&Ba(b)};\\n\\\n        _.Na=function(a,b,c,d){a.o&&(a.o=void 0);if(b>=a.j||d)return Ma(a)[b]=c,a;a.Da[b+a.Ac]=c;(c=a.Jb)&&b\\\n        \\ in c&&delete c[b];return a};_.Oa=function(a,b){return null==a?b:a};Ra=function(a){const\\\n        \\ b=_.ta(a);if(b&2)return a;a=_.Pa(a,Qa);za(b,a);Object.freeze(a);return a};\\n\\\n        Ua=function(a,b,c=za){if(null!=a){if(oa&&a instanceof Uint8Array)return a.length?new\\\n        \\ _.Fa(new Uint8Array(a),_.Sa):_.Ta();if(Array.isArray(a)){const d=_.ta(a);if(d&2)return\\\n        \\ a;if(b&&!(d&32)&&(d&16||0===d))return _.ua(a,d|2),a;a=_.Ha(a,Ua,c,!0);b=_.ta(a);b&4&&b&2&&Object.freeze(a);return\\\n        \\ a}return a.Wd===_.Ja?Qa(a):a}};Qa=function(a){if(_.wa(a.Da))return a;a=Va(a,!0);_.ra(a.Da,2);return\\\n        \\ a};\\nVa=function(a,b){var c=a.Da,d=_.xa([]),e=a.constructor.j;e&&d.push(e);e=a.Jb;let\\\n        \\ f;e&&(d.length=c.length,d.fill(void 0,d.length,c.length),f={},d[d.length-1]=f);0!==(_.ta(c)&128)&&Ba(d);b=b||a.nc()?za:_.ya;d=_.Da(a.constructor,d);a.tc&&(d.tc=a.tc.slice());const\\\n        \\ g=!!(_.ta(c)&16);var h=e?c.length-1:c.length;for(let w=0;w<h;w++){var k=d,q=w-a.Ac,p=c[w],t=g,z=b;const\\\n        \\ D=a.Za&&a.Za[q];D?_.Wa(k,q,Ra(D),!1):_.r(k,q,Ua(p,t,z),!1)}if(e)for(const\\\n        \\ w in e)k=e[w],h=+w,Number.isNaN(h)?f[h]=k:(c=d,q=g,p=b,(t=a.Za&&\\na.Za[h])?_.Wa(c,h,Ra(t),!0):_.r(c,h,Ua(k,q,p),!0));return\\\n        \\ d};_.Xa=function(a){if(!_.wa(a.Da))return a;const b=Va(a,!1);b.o=a;return\\\n        \\ b};Ya=function(a,b){if(Array.isArray(a)){var c=_.ta(a),d=1;!b||c&2||(d|=16);(c&d)!==d&&_.ua(a,c|d)}};Za=function(a,b){return\\\n        \\ Ga(b)};_.u=function(a,b){return null!=a?!!a:!!b};_.v=function(a,b){void\\\n        \\ 0==b&&(b=\\\"\\\");return null!=a?a:b};_.$a=function(a,b){void 0==b&&(b=0);return\\\n        \\ null!=a?a:b};_.ab=function(a,b,c){for(const d in a)b.call(c,a[d],d,a)};\\n\\\n        _.cb=function(a,b){let c,d;for(let e=1;e<arguments.length;e++){d=arguments[e];for(c\\\n        \\ in d)a[c]=d[c];for(let f=0;f<bb.length;f++)c=bb[f],Object.prototype.hasOwnProperty.call(d,c)&&(a[c]=d[c])}};\\n\\\n        var gb,hb,ib;_.db=_.db||{};_.m=this||self;_.eb=function(a){var b=typeof a;return\\\"\\\n        object\\\"==b&&null!=a||\\\"function\\\"==b};_.fb=\\\"closure_uid_\\\"+(1E9*Math.random()>>>0);gb=function(a,b,c){return\\\n        \\ a.call.apply(a.bind,arguments)};hb=function(a,b,c){if(!a)throw Error();if(2<arguments.length){var\\\n        \\ d=Array.prototype.slice.call(arguments,2);return function(){var e=Array.prototype.slice.call(arguments);Array.prototype.unshift.apply(e,d);return\\\n        \\ a.apply(b,e)}}return function(){return a.apply(b,arguments)}};\\n_.x=function(a,b,c){Function.prototype.bind&&-1!=Function.prototype.bind.toString().indexOf(\\\"\\\n        native code\\\")?_.x=gb:_.x=hb;return _.x.apply(null,arguments)};_.y=function(a,b){a=a.split(\\\"\\\n        .\\\");var c=_.m;a[0]in c||\\\"undefined\\\"==typeof c.execScript||c.execScript(\\\"\\\n        var \\\"+a[0]);for(var d;a.length&&(d=a.shift());)a.length||void 0===b?c[d]&&c[d]!==Object.prototype[d]?c=c[d]:c=c[d]={}:c[d]=b};\\n\\\n        _.A=function(a,b){function c(){}c.prototype=b.prototype;a.Z=b.prototype;a.prototype=new\\\n        \\ c;a.prototype.constructor=a;a.yi=function(d,e,f){for(var g=Array(arguments.length-2),h=2;h<arguments.length;h++)g[h-2]=arguments[h];return\\\n        \\ b.prototype[e].apply(d,g)}};ib=function(a){return a};_.jb=function(a){var\\\n        \\ b=null,c=_.m.trustedTypes;if(!c||!c.createPolicy)return b;try{b=c.createPolicy(a,{createHTML:ib,createScript:ib,createScriptURL:ib})}catch(d){_.m.console&&_.m.console.error(d.message)}return\\\n        \\ b};\\n_.A(_.aa,Error);_.aa.prototype.name=\\\"CustomError\\\";\\n_.kb=String.prototype.trim?function(a){return\\\n        \\ a.trim()}:function(a){return/^[\\\\s\\\\xa0]*([\\\\s\\\\S]*?)[\\\\s\\\\xa0]*$/.exec(a)[1]};\\n\\\n        _.lb=function(a,b){return Array.prototype.indexOf.call(a,b,void 0)};_.mb=function(a,b,c){Array.prototype.forEach.call(a,b,c)};_.Pa=function(a,b,c){return\\\n        \\ Array.prototype.map.call(a,b,c)};\\n_.nb=function(a){_.nb[\\\" \\\"](a);return\\\n        \\ a};_.nb[\\\" \\\"]=function(){};\\nvar Bb,Cb,Hb;_.ob=_.ca();_.B=_.da();_.pb=_.n(\\\"\\\n        Edge\\\");_.qb=_.pb||_.B;_.rb=_.n(\\\"Gecko\\\")&&!_.na()&&!(_.n(\\\"Trident\\\")||_.n(\\\"\\\n        MSIE\\\"))&&!_.n(\\\"Edge\\\");_.sb=_.na();_.tb=_.n(\\\"Macintosh\\\");_.ub=_.n(\\\"Windows\\\"\\\n        );_.vb=_.n(\\\"Linux\\\")||_.n(\\\"CrOS\\\");_.wb=_.n(\\\"Android\\\");_.xb=ja();_.yb=_.n(\\\"\\\n        iPad\\\");_.zb=_.n(\\\"iPod\\\");_.Ab=_.la();Bb=function(){var a=_.m.document;return\\\n        \\ a?a.documentMode:void 0};\\na:{var Db=\\\"\\\",Eb=function(){var a=_.ba();if(_.rb)return/rv:([^\\\\\\\n        );]+)(\\\\)|;)/.exec(a);if(_.pb)return/Edge\\\\/([\\\\d\\\\.]+)/.exec(a);if(_.B)return/\\\\\\\n        b(?:MSIE|rv)[: ]([^\\\\);]+)(\\\\)|;)/.exec(a);if(_.sb)return/WebKit\\\\/(\\\\S+)/.exec(a);if(_.ob)return/(?:Version)[\\\n        \\ \\\\/]?(\\\\S+)/.exec(a)}();Eb&&(Db=Eb?Eb[1]:\\\"\\\");if(_.B){var Fb=Bb();if(null!=Fb&&Fb>parseFloat(Db)){Cb=String(Fb);break\\\n        \\ a}}Cb=Db}_.Gb=Cb;if(_.m.document&&_.B){var Ib=Bb();Hb=Ib?Ib:parseInt(_.Gb,10)||void\\\n        \\ 0}else Hb=void 0;_.Jb=Hb;\\n_.Kb=_.ea();_.Lb=ja()||_.n(\\\"iPod\\\");_.Mb=_.n(\\\"\\\n        iPad\\\");_.Nb=_.ia();_.Ob=_.fa();_.Pb=_.ha()&&!_.la();\\nvar Qb;Qb={};_.Rb=null;_.Ea=function(a,b){void\\\n        \\ 0===b&&(b=0);_.Sb();b=Qb[b];const c=Array(Math.floor(a.length/3)),d=b[64]||\\\"\\\n        \\\";let e=0,f=0;for(;e<a.length-2;e+=3){var g=a[e],h=a[e+1],k=a[e+2],q=b[g>>2];g=b[(g&3)<<4|h>>4];h=b[(h&15)<<2|k>>6];k=b[k&63];c[f++]=q+g+h+k}q=0;k=d;switch(a.length-e){case\\\n        \\ 2:q=a[e+1],k=b[(q&15)<<2]||d;case 1:a=a[e],c[f]=b[a>>2]+b[(a&3)<<4|q>>4]+k+d}return\\\n        \\ c.join(\\\"\\\")};\\n_.Sb=function(){if(!_.Rb){_.Rb={};for(var a=\\\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\\\"\\\n        .split(\\\"\\\"),b=[\\\"+/=\\\",\\\"+/\\\",\\\"-_=\\\",\\\"-_.\\\",\\\"-_\\\"],c=0;5>c;c++){var d=a.concat(b[c].split(\\\"\\\n        \\\"));Qb[c]=d;for(var e=0;e<d.length;e++){var f=d[e];void 0===_.Rb[f]&&(_.Rb[f]=e)}}}};\\n\\\n        var oa;oa=\\\"undefined\\\"!==typeof Uint8Array;_.Sa={};\\n_.Tb=\\\"undefined\\\"!==typeof\\\n        \\ TextDecoder;_.Ub=\\\"undefined\\\"!==typeof TextEncoder;\\nvar Vb;_.Ta=function(){return\\\n        \\ Vb||(Vb=new _.Fa(null,_.Sa))};_.Fa=class{constructor(a,b){if(b!==_.Sa)throw\\\n        \\ Error(\\\"m\\\");this.Ea=a;if(null!=a&&0===a.length)throw Error(\\\"n\\\");}mc(){return\\\n        \\ null==this.Ea}};\\nvar qa=Symbol();\\nvar Wb,Yb;_.Ja={};Yb=[];_.ua(Yb,23);_.Xb=Object.freeze(Yb);_.Zb=function(a){if(_.wa(a.Da))throw\\\n        \\ Error(\\\"p\\\");};\\nvar Ma;Ma=function(a){const b=a.j+a.Ac;return a.Jb||(a.Jb=a.Da[b]={})};_.C=function(a,b,c){return-1===b?null:b>=a.j?a.Jb?a.Jb[b]:void\\\n        \\ 0:c&&a.Jb&&(c=a.Jb[b],null!=c)?c:a.Da[b+a.Ac]};_.r=function(a,b,c,d){_.Zb(a);return\\\n        \\ _.Na(a,b,c,d)};_.$b=function(a,b){return null!=_.C(a,b,!1)};_.E=function(a,b){a=_.C(a,b);return\\\n        \\ null==a?a:!!a};\\n_.ac=function(a,b,c,d){const e=_.C(a,c,d);{let g=!1;var\\\n        \\ f=null==e||\\\"object\\\"!==typeof e||(g=Array.isArray(e))||e.Wd!==_.Ja?g?new\\\n        \\ b(e):void 0:e}f!==e&&null!=f&&(_.Na(a,c,f,d),_.ra(f.Da,_.ta(a.Da)&18));return\\\n        \\ f};_.F=function(a,b,c,d=!1){b=_.ac(a,b,c,d);if(null==b)return b;if(!_.wa(a.Da)){var\\\n        \\ e=_.Xa(b);e!==b&&(b=e,_.Na(a,c,b,d))}return b};_.G=function(a,b,c){_.Zb(a);null==c&&(c=void\\\n        \\ 0);return _.Na(a,b,c)};\\n_.Wa=function(a,b,c,d){_.Zb(a);let e;if(null!=c){e=_.va([]);let\\\n        \\ f=!1;for(let g=0;g<c.length;g++)e[g]=c[g].Da,f=f||_.wa(e[g]);a.Za||(a.Za={});a.Za[b]=c;c=e;f?_.sa(c,8):_.ra(c,8)}else\\\n        \\ a.Za&&(a.Za[b]=void 0),e=_.Xb;return _.Na(a,b,e,d)};_.bc=function(a,b,c=0){a=_.C(a,b);return\\\n        \\ _.Oa(null==a?a:+a,c)};_.cc=function(a,b,c=0){return _.Oa(_.C(a,b),c)};\\n\\\n        _.H=class{constructor(a,b,c){null==a&&(a=_.Ca);_.Ca=void 0;var d=this.constructor.o||0,e=0<d,f=this.constructor.j,g=!1;if(null==a){a=f?[f]:[];var\\\n        \\ h=!0;_.ua(a,48)}else{if(!Array.isArray(a))throw Error();if(f&&f!==a[0])throw\\\n        \\ Error();const k=_.ra(a,0);let q=k;if(h=0!==(16&q))(g=0!==(32&q))||(q|=32);if(e)if(128&q)d=0;else{if(0<a.length){const\\\n        \\ p=a[a.length-1];if(Aa(p)&&\\\"g\\\"in p){d=0;q|=128;delete p.g;let t=!0;for(let\\\n        \\ z in p){t=!1;break}t&&a.pop()}}}else if(128&q)throw Error();k!==q&&_.ua(a,q)}this.Ac=(f?\\n\\\n        0:-1)-d;this.Za=void 0;this.Da=a;a:{f=this.Da.length;d=f-1;if(f&&(f=this.Da[d],Aa(f))){this.Jb=f;this.j=d-this.Ac;break\\\n        \\ a}void 0!==b&&-1<b?(this.j=Math.max(b,d+1-this.Ac),this.Jb=void 0):this.j=Number.MAX_VALUE}if(!e&&this.Jb&&\\\"\\\n        g\\\"in this.Jb)throw Error(\\\"t\\\");if(c){b=h&&!g&&!0;e=this.j;let k;for(h=0;h<c.length;h++)g=c[h],g<e?(g+=this.Ac,(d=a[g])?Ya(d,b):a[g]=_.Xb):(k||(k=Ma(this)),(d=k[g])?Ya(d,b):k[g]=_.Xb)}}toJSON(){const\\\n        \\ a=this.Da;return Wb?a:_.Ha(a,Ka,La)}Ha(){Wb=!0;try{return JSON.stringify(this.toJSON(),\\n\\\n        Za)}finally{Wb=!1}}nc(){return _.wa(this.Da)}};_.H.prototype.Wd=_.Ja;_.H.prototype.toString=function(){return\\\n        \\ this.Da.toString()};\\n_.dc=Symbol();_.ec=Symbol();_.fc=Symbol();_.gc=Symbol();\\n\\\n        var hc=class extends _.H{constructor(){super()}};\\n_.ic=class extends _.H{constructor(){super()}Xc(a){return\\\n        \\ _.r(this,3,a)}};\\n_.jc=class extends _.H{constructor(a){super(a)}};\\nvar\\\n        \\ kc=class extends _.H{constructor(a){super(a)}};\\n_.lc=class extends _.H{constructor(a){super(a)}Yc(a){return\\\n        \\ _.r(this,24,a)}};\\n_.mc=class extends _.H{constructor(a){super(a)}};\\n_.I=function(){this.Gb=this.Gb;this.Ra=this.Ra};_.I.prototype.Gb=!1;_.I.prototype.isDisposed=function(){return\\\n        \\ this.Gb};_.I.prototype.ya=function(){this.Gb||(this.Gb=!0,this.R())};_.I.prototype.R=function(){if(this.Ra)for(;this.Ra.length;)this.Ra.shift()()};\\n\\\n        var nc=class extends _.I{constructor(){var a=window;super();this.A=a;this.j=[];this.o={}}resolve(a){var\\\n        \\ b=this.A;a=a.split(\\\".\\\");for(var c=a.length,d=0;d<c;++d)if(b[a[d]])b=b[a[d]];else\\\n        \\ return null;return b instanceof Function?b:null}qd(){for(var a=this.j.length,b=this.j,c=[],d=0;d<a;++d){var\\\n        \\ e=b[d].j(),f=this.resolve(e);if(f&&f!=this.o[e])try{b[d].qd(f)}catch(g){}else\\\n        \\ c.push(b[d])}this.j=c.concat(b.slice(a))}};\\nvar oc=class extends _.I{constructor(){var\\\n        \\ a=_.J;super();this.A=a;this.C=this.j=null;this.B=0;this.D={};this.o=!1;a=window.navigator.userAgent;0<=a.indexOf(\\\"\\\n        MSIE\\\")&&0<=a.indexOf(\\\"Trident\\\")&&(a=/\\\\b(?:MSIE|rv)[: ]([^\\\\);]+)(\\\\)|;)/.exec(a))&&a[1]&&9>parseFloat(a[1])&&(this.o=!0)}F(a,b){this.j=b;this.C=a;b.preventDefault?b.preventDefault():b.returnValue=!1}};\\n\\\n        _.pc=class extends _.H{constructor(a){super(a)}};\\n_.qc=class extends _.H{constructor(a){super(a)}};\\n\\\n        _.rc=class{constructor(){this.data={}}Ha(a){var b=[],c;for(c in this.data)b.push(encodeURIComponent(c)+\\\"\\\n        =\\\"+encodeURIComponent(String(this.data[c])));return(\\\"atyp=i&zx=\\\"+(new Date).getTime()+\\\"\\\n        &\\\"+b.join(\\\"&\\\")).substr(0,a)}};\\nvar sc=class extends _.rc{constructor(a,b){super();var\\\n        \\ c=_.F(a,kc,8)||new kc;window.google&&window.google.kEI&&(this.data.ei=window.google.kEI);this.data.sei=_.v(_.C(a,10));this.data.ogf=_.v(_.C(c,3));this.data.ogrp=(window.google&&window.google.sn?!/.*hp$/.test(window.google.sn):_.u(_.E(a,7)))?\\\"\\\n        1\\\":\\\"\\\";this.data.ogv=_.v(_.C(c,6))+\\\".\\\"+_.v(_.C(c,7));this.data.ogd=_.v(_.C(a,21));this.data.ogc=_.v(_.C(a,20));this.data.ogl=_.v(_.C(a,5));b&&(this.data.oggv=b)}};\\n\\\n        var bb=\\\"constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString\\\n        \\ toString valueOf\\\".split(\\\" \\\");\\n_.tc=class extends sc{constructor(a,b,c,d,e){super(a,b);_.cb(this.data,{jexpid:_.v(_.C(a,9)),srcpg:\\\"\\\n        prop=\\\"+_.v(_.C(a,6)),jsr:Math.round(1/d),emsg:c.name+\\\":\\\"+c.message});if(e){e._sn&&(e._sn=\\\"\\\n        og.\\\"+e._sn);for(const f in e)this.data[encodeURIComponent(f)]=e[f]}}};\\n\\\n        var uc;_.vc=function(){void 0===uc&&(uc=_.jb(\\\"ogb-qtm#html\\\"));return uc};\\n\\\n        _.yc=function(a,b){this.j=a===_.wc&&b||\\\"\\\";this.o=_.xc};_.yc.prototype.Yb=!0;_.yc.prototype.Ib=function(){return\\\n        \\ this.j};_.xc={};_.wc={};\\n_.Ac=class{constructor(a,b){this.j=b===_.zc?a:\\\"\\\n        \\\"}toString(){return this.j+\\\"\\\"}};_.Ac.prototype.Yb=!0;_.Ac.prototype.Ib=function(){return\\\n        \\ this.j.toString()};_.Cc=function(a){return _.Bc(a).toString()};_.Bc=function(a){return\\\n        \\ a instanceof _.Ac&&a.constructor===_.Ac?a.j:\\\"type_error:TrustedResourceUrl\\\"\\\n        };_.zc={};\\nvar Gc,Hc,Dc;_.Ec=class{constructor(a,b){this.j=b===Dc?a:\\\"\\\"\\\n        }toString(){return this.j.toString()}};_.Ec.prototype.Yb=!0;_.Ec.prototype.Ib=function(){return\\\n        \\ this.j.toString()};_.Fc=function(a){return a instanceof _.Ec&&a.constructor===_.Ec?a.j:\\\"\\\n        type_error:SafeUrl\\\"};Gc=/^data:(.*);base64,[a-z0-9+\\\\/]+=*$/i;Hc=/^(?:(?:https?|mailto|ftp):|[^:/?#]*(?:[/?#]|$))/i;\\n\\\n        _.Jc=function(a){if(a instanceof _.Ec)return a;a=\\\"object\\\"==typeof a&&a.Yb?a.Ib():String(a);Hc.test(a)?a=_.Ic(a):(a=String(a).replace(/(%0A|%0D)/g,\\\"\\\n        \\\"),a=a.match(Gc)?_.Ic(a):null);return a};_.Kc=function(a){if(a instanceof\\\n        \\ _.Ec)return a;a=\\\"object\\\"==typeof a&&a.Yb?a.Ib():String(a);Hc.test(a)||(a=\\\"\\\n        about:invalid#zClosurez\\\");return _.Ic(a)};Dc={};_.Ic=function(a){return new\\\n        \\ _.Ec(a,Dc)};_.Lc=_.Ic(\\\"about:invalid#zClosurez\\\");\\n_.Mc={};_.Nc=class{constructor(a,b){this.j=b===_.Mc?a:\\\"\\\n        \\\";this.Yb=!0}Ib(){return this.j}toString(){return this.j.toString()}};_.Pc=new\\\n        \\ _.Nc(\\\"\\\",_.Mc);_.Qc=RegExp(\\\"^[-,.\\\\\\\"'%_!#/ a-zA-Z0-9\\\\\\\\[\\\\\\\\]]+$\\\");_.Rc=RegExp(\\\"\\\n        \\\\\\\\b(url\\\\\\\\([ \\\\t\\\\n]*)('[ -&(-\\\\\\\\[\\\\\\\\]-~]*'|\\\\\\\"[ !#-\\\\\\\\[\\\\\\\\]-~]*\\\\\\\n        \\\"|[!#-&*-\\\\\\\\[\\\\\\\\]-~]*)([ \\\\t\\\\n]*\\\\\\\\))\\\",\\\"g\\\");\\n_.Sc=RegExp(\\\"\\\\\\\\b(calc|cubic-bezier|fit-content|hsl|hsla|linear-gradient|matrix|minmax|radial-gradient|repeat|rgb|rgba|(rotate|scale|translate)(X|Y|Z|3d)?|steps|var)\\\\\\\n        \\\\([-+*/0-9a-zA-Z.%#\\\\\\\\[\\\\\\\\], ]+\\\\\\\\)\\\",\\\"g\\\");\\nvar Tc;Tc={};_.Vc=function(a){return\\\n        \\ a instanceof _.Uc&&a.constructor===_.Uc?a.j:\\\"type_error:SafeHtml\\\"};_.Wc=function(a){const\\\n        \\ b=_.vc();a=b?b.createHTML(a):a;return new _.Uc(a,Tc)};_.Uc=class{constructor(a,b){this.j=b===Tc?a:\\\"\\\n        \\\";this.Yb=!0}Ib(){return this.j.toString()}toString(){return this.j.toString()}};_.Xc=new\\\n        \\ _.Uc(_.m.trustedTypes&&_.m.trustedTypes.emptyHTML||\\\"\\\",Tc);_.Yc=_.Wc(\\\"\\\n        <br>\\\");\\nvar $c;_.Zc=function(a){let b=!1,c;return function(){b||(c=a(),b=!0);return\\\n        \\ c}}(function(){var a=document.createElement(\\\"div\\\"),b=document.createElement(\\\"\\\n        div\\\");b.appendChild(document.createElement(\\\"div\\\"));a.appendChild(b);b=a.firstChild.firstChild;a.innerHTML=_.Vc(_.Xc);return!b.parentElement});$c=/^[\\\\\\\n        w+/_-]+[=]{0,2}$/;\\n_.ad=function(a){a=(a||_.m).document;return a.querySelector?(a=a.querySelector('style[nonce],link[rel=\\\"\\\n        stylesheet\\\"][nonce]'))&&(a=a.nonce||a.getAttribute(\\\"nonce\\\"))&&$c.test(a)?a:\\\"\\\n        \\\":\\\"\\\"};\\n_.bd=RegExp(\\\"^\\\\\\\\s{3,4}at(?: (?:(.*?)\\\\\\\\.)?((?:new )?(?:[a-zA-Z_$][\\\\\\\n        \\\\w$]*|<anonymous>))(?: \\\\\\\\[as ([a-zA-Z_$][\\\\\\\\w$]*)\\\\\\\\])?)? (?:\\\\\\\\(unknown\\\n        \\ source\\\\\\\\)|\\\\\\\\(native\\\\\\\\)|\\\\\\\\((?:eval at )?((?:http|https|file)://[^\\\\\\\n        \\\\s)]+|javascript:.*)\\\\\\\\)|((?:http|https|file)://[^\\\\\\\\s)]+|javascript:.*))$\\\"\\\n        );_.cd=RegExp(\\\"^(?:(.*?)\\\\\\\\.)?([a-zA-Z_$][\\\\\\\\w$]*(?:/.?<)?)?(\\\\\\\\(.*\\\\\\\\\\\n        ))?@(?::0|((?:http|https|file)://[^\\\\\\\\s)]+|javascript:.*))$\\\");\\nvar dd,gd,fd;_.ed=function(a){let\\\n        \\ b;b=window.google&&window.google.logUrl?\\\"\\\":\\\"https://www.google.com\\\"\\\n        ;b+=\\\"/gen_204?use_corp=on&\\\";b+=a.Ha(2040-b.length);dd(_.Jc(b)||_.Lc)};dd=function(a){var\\\n        \\ b=new Image,c=fd;b.onerror=b.onload=b.onabort=function(){c in gd&&delete\\\n        \\ gd[c]};gd[fd++]=b;b.src=_.Fc(a)};gd=[];fd=0;\\n_.hd=class extends _.H{constructor(a){super(a)}};\\n\\\n        _.id=a=>{var b=\\\"Gc\\\";if(a.Gc&&a.hasOwnProperty(b))return a.Gc;b=new a;return\\\n        \\ a.Gc=b};\\nvar pd,ld,nd;_.md=function(a,b){var c=_.kd.j();if(a in c.j){if(c.j[a]!=b)throw\\\n        \\ new ld;}else{c.j[a]=b;if(b=c.o[a])for(let d=0,e=b.length;d<e;d++)b[d].j(c.j,a);delete\\\n        \\ c.o[a]}};_.od=function(a,b){if(b in a.j)return a.j[b];throw new nd;};_.kd=class{constructor(){this.j={};this.o={}}static\\\n        \\ j(){return _.id(_.kd)}};pd=class extends _.aa{constructor(){super()}};ld=class\\\n        \\ extends pd{};nd=class extends pd{};\\nvar sd=class{constructor(){var a=qd;this.C=rd;this.o=_.$a(_.bc(a,2,.001),.001);this.D=_.u(_.E(a,1))&&Math.random()<this.o;this.F=_.$a(_.cc(a,3,1),1);this.B=0;this.j=this.A=null}log(a,b){if(this.j){const\\\n        \\ d=new hc;_.r(d,1,a.message);_.r(d,2,a.stack);_.r(d,3,a.lineNumber);_.r(d,5,1);const\\\n        \\ e=new _.ic;_.G(e,40,d);this.j.log(98,e)}try{if(this.D&&this.B<this.F){try{var\\\n        \\ c=(this.A||_.od(_.kd.j(),\\\"lm\\\")).B(a,b)}catch(d){c=new _.tc(this.C,\\\"quantum:gapiBuildLabel\\\"\\\n        ,a,this.o,b)}_.ed(c);this.B++}}catch(d){}}};\\nvar td=[1,2,3,4,5,6,9,10,11,13,14,28,29,30,34,35,37,38,39,40,42,43,48,49,50,51,52,53,62,500],vd=function(a){if(!ud){ud={};for(var\\\n        \\ b=0;b<td.length;b++)ud[td[b]]=!0}return!!ud[a]},wd=function(a){a=String(a);return\\\n        \\ a.replace(\\\".\\\",\\\"%2E\\\").replace(\\\",\\\",\\\"%2C\\\")},xd=class extends sc{constructor(a,b,c,d,e){super(a,\\\"\\\n        quantum:gapiBuildLabel\\\");_.cb(this.data,{oge:c,ogex:_.v(_.C(a,9)),ogp:_.v(_.C(a,6)),ogsr:Math.round(1/(vd(c)?_.$a(_.bc(b,3,1)):_.$a(_.bc(b,2,1E-4)))),ogus:d});if(e){\\\"\\\n        ogw\\\"in e&&(this.data.ogw=e.ogw,\\ndelete e.ogw);\\\"ved\\\"in e&&(this.data.ved=e.ved,delete\\\n        \\ e.ved);a=[];for(var f in e)0!=a.length&&a.push(\\\",\\\"),a.push(wd(f)),a.push(\\\"\\\n        .\\\"),a.push(wd(e[f]));e=a.join(\\\"\\\");\\\"\\\"!=e&&(this.data.ogad=e)}}},ud=null;\\n\\\n        var yd=class extends _.H{constructor(a){super(a)}};\\nvar Cd=class{constructor(){var\\\n        \\ a=zd,b=Ad,c=Bd;this.o=a;this.j=b;this.B=_.$a(_.bc(a,2,1E-4),1E-4);this.D=_.$a(_.bc(a,3,1),1);b=Math.random();this.A=_.u(_.E(a,1))&&b<this.B;this.C=_.u(_.E(a,1))&&b<this.D;a=0;_.u(_.E(c,1))&&(a|=1);_.u(_.E(c,2))&&(a|=2);_.u(_.E(c,3))&&(a|=4);this.F=a}log(a,b){try{if(vd(a)?this.C:this.A){var\\\n        \\ c=new xd(this.j,this.o,a,this.F,b);_.ed(c)}}catch(d){}}};\\nvar Ed;_.Dd=function(a){if(0<a.o.length){var\\\n        \\ b=void 0!==a.Ea,c=void 0!==a.j;if(b||c){b=b?a.A:a.B;c=a.o;a.o=[];try{_.mb(c,b,a)}catch(d){console.error(d)}}}};_.Fd=class{constructor(a){this.Ea=a;this.j=void\\\n        \\ 0;this.o=[]}then(a,b,c){this.o.push(new Ed(a,b,c));_.Dd(this)}resolve(a){if(void\\\n        \\ 0!==this.Ea||void 0!==this.j)throw Error(\\\"B\\\");this.Ea=a;_.Dd(this)}A(a){a.o&&a.o.call(a.j,this.Ea)}B(a){a.A&&a.A.call(a.j,this.j)}};Ed=class{constructor(a,b,c){this.o=a;this.A=b;this.j=c}};\\n\\\n        _.K=class{constructor(){this.B=new _.Fd;this.j=new _.Fd;this.G=new _.Fd;this.D=new\\\n        \\ _.Fd;this.F=new _.Fd;this.H=new _.Fd;this.C=new _.Fd;this.A=new _.Fd;this.o=new\\\n        \\ _.Fd;this.J=new _.Fd}N(){return this.B}T(){return this.j}Gb(){return this.G}Ra(){return\\\n        \\ this.D}S(){return this.F}O(){return this.H}P(){return this.C}M(){return\\\n        \\ this.A}L(){return this.o}static j(){return _.id(_.K)}};\\nvar Kd;_.Hd=function(){return\\\n        \\ _.F(_.Gd,_.lc,1)};_.Id=function(){return _.F(_.Gd,_.mc,5)};Kd=class extends\\\n        \\ _.H{constructor(){super(Jd)}};\\nvar Jd;window.gbar_&&window.gbar_.CONFIG?Jd=window.gbar_.CONFIG[0]||{}:Jd=[];_.Gd=new\\\n        \\ Kd;\\nvar qd,rd,Ad,Bd,zd;qd=_.F(_.Gd,_.hd,3)||new _.hd;rd=_.Hd()||new _.lc;_.J=new\\\n        \\ sd;Ad=_.Hd()||new _.lc;Bd=_.Id()||new _.mc;zd=_.F(_.Gd,yd,4)||new yd;_.Ld=new\\\n        \\ Cd;\\n_.y(\\\"gbar_._DumpException\\\",function(a){_.J?_.J.log(a):console.error(a)});\\n\\\n        _.Md=new oc;\\n_.Ld.log(8,{m:\\\"BackCompat\\\"==document.compatMode?\\\"q\\\":\\\"s\\\"\\\n        });_.y(\\\"gbar.A\\\",_.Fd);_.Fd.prototype.aa=_.Fd.prototype.then;_.y(\\\"gbar.B\\\"\\\n        ,_.K);_.K.prototype.ba=_.K.prototype.T;_.K.prototype.bb=_.K.prototype.Gb;_.K.prototype.bd=_.K.prototype.S;_.K.prototype.bf=_.K.prototype.N;_.K.prototype.bg=_.K.prototype.Ra;_.K.prototype.bh=_.K.prototype.O;_.K.prototype.bi=_.K.prototype.P;_.K.prototype.bj=_.K.prototype.M;_.K.prototype.bk=_.K.prototype.L;_.y(\\\"\\\n        gbar.a\\\",_.K.j());var Nd=new nc;_.md(\\\"api\\\",Nd);var Od=_.Id()||new _.mc;\\n\\\n        window.__PVT=_.v(_.C(Od,8));_.md(\\\"eq\\\",_.Md);\\n\\n}catch(e){_._DumpException(e)}\\n\\\n        try{\\nvar Pd=class extends _.H{constructor(){super()}};\\nvar Qd=class extends\\\n        \\ _.I{constructor(){super();this.o=[];this.j=[]}A(a,b){this.o.push({features:a,options:b})}init(a,b,c){window.gapi={};var\\\n        \\ d=window.___jsl={};d.h=_.v(_.C(a,1));_.$b(a,12)&&(d.dpo=_.u(_.E(a,12)));d.ms=_.v(_.C(a,2));d.m=_.v(_.C(a,3));d.l=[];_.C(b,1)&&(a=_.C(b,3))&&this.j.push(a);_.C(c,1)&&(c=_.C(c,2))&&this.j.push(c);_.y(\\\"\\\n        gapi.load\\\",(0,_.x)(this.A,this));return this}};\\nvar Rd=_.F(_.Gd,_.pc,14)||new\\\n        \\ _.pc,Sd=_.F(_.Gd,_.qc,9)||new _.qc,Td=new Pd,Ud=new Qd;Ud.init(Rd,Sd,Td);_.md(\\\"\\\n        gs\\\",Ud);\\n\\n}catch(e){_._DumpException(e)}\\n})(this.gbar_);\\n// Google Inc.\\n\\\n        ;});})();(function(){window.jsl.dh('_QyS5Y_SSL5HO5OUPp_mggAY_5','\\\\x3cdiv\\\n        \\ jscontroller\\\\x3d\\\\x22w4UyN\\\\x22 class\\\\x3d\\\\x22fLciMb\\\\x22 data-po\\\\x3d\\\\\\\n        x22360\\\\x22 aria-label\\\\x3d\\\\x22Setelan\\\\x22 role\\\\x3d\\\\x22button\\\\x22 tabindex\\\\\\\n        x3d\\\\x220\\\\x22 jsaction\\\\x3d\\\\x22rcuQ6b:npT2md;HfCvm;mouseenter:eGiyHb;mouseleave:LfDNce\\\\\\\n        x22 data-ved\\\\x3d\\\\x220ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ6psICBI\\\\x22\\\\x3e\\\\\\\n        x3cspan class\\\\x3d\\\\x22z1asCe E9hVAb\\\\x22\\\\x3e\\\\x3csvg focusable\\\\x3d\\\\x22false\\\\\\\n        x22 xmlns\\\\x3d\\\\x22http://www.w3.org/2000/svg\\\\x22 viewBox\\\\x3d\\\\x220 0 24\\\n        \\ 24\\\\x22\\\\x3e\\\\x3cpath d\\\\x3d\\\\x22M13.85 22.25h-3.7c-.74 0-1.36-.54-1.45-1.27l-.27-1.89c-.27-.14-.53-.29-.79-.46l-1.8.72c-.7.26-1.47-.03-1.81-.65L2.2\\\n        \\ 15.53c-.35-.66-.2-1.44.36-1.88l1.53-1.19c-.01-.15-.02-.3-.02-.46 0-.15.01-.31.02-.46l-1.52-1.19c-.59-.45-.74-1.26-.37-1.88l1.85-3.19c.34-.62\\\n        \\ 1.11-.9 1.79-.63l1.81.73c.26-.17.52-.32.78-.46l.27-1.91c.09-.7.71-1.25 1.44-1.25h3.7c.74\\\n        \\ 0 1.36.54 1.45 1.27l.27 1.89c.27.14.53.29.79.46l1.8-.72c.71-.26 1.48.03\\\n        \\ 1.82.65l1.84 3.18c.36.66.2 1.44-.36 1.88l-1.52 1.19c.01.15.02.3.02.46s-.01.31-.02.46l1.52\\\n        \\ 1.19c.56.45.72 1.23.37 1.86l-1.86 3.22c-.34.62-1.11.9-1.8.63l-1.8-.72c-.26.17-.52.32-.78.46l-.27\\\n        \\ 1.91c-.1.68-.72 1.22-1.46 1.22zm-3.23-2h2.76l.37-2.55.53-.22c.44-.18.88-.44\\\n        \\ 1.34-.78l.45-.34 2.38.96 1.38-2.4-2.03-1.58.07-.56c.03-.26.06-.51.06-.78s-.03-.53-.06-.78l-.07-.56\\\n        \\ 2.03-1.58-1.39-2.4-2.39.96-.45-.35c-.42-.32-.87-.58-1.33-.77l-.52-.22-.37-2.55h-2.76l-.37\\\n        \\ 2.55-.53.21c-.44.19-.88.44-1.34.79l-.45.33-2.38-.95-1.39 2.39 2.03 1.58-.07.56a7\\\n        \\ 7 0 0 0-.06.79c0 .26.02.53.06.78l.07.56-2.03 1.58 1.38 2.4 2.39-.96.45.35c.43.33.86.58\\\n        \\ 1.33.77l.53.22.38 2.55z\\\\x22\\\\x3e\\\\x3c/path\\\\x3e\\\\x3ccircle cx\\\\x3d\\\\x2212\\\\\\\n        x22 cy\\\\x3d\\\\x2212\\\\x22 r\\\\x3d\\\\x223.5\\\\x22\\\\x3e\\\\x3c/circle\\\\x3e\\\\x3c/svg\\\\\\\n        x3e\\\\x3c/span\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22suEOdc\\\\x22 class\\\\x3d\\\\x22ZOyvub\\\\\\\n        x22\\\\x3eSetelan Cepat\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e');})();(function(){window.jsl.dh('_QyS5Y_SSL5HO5OUPp_mggAY_7','\\\\\\\n        x3cspan class\\\\x3d\\\\x22gb\\\\x22 style\\\\x3d\\\\x22display:none\\\\x22\\\\x3e\\\\x3c/span\\\\\\\n        x3e\\\\x3cdiv class\\\\x3d\\\\x22gb_ua gb_3d gb_6a\\\\x22 id\\\\x3d\\\\x22gb\\\\x22\\\\x3e\\\\\\\n        x3cdiv class\\\\x3d\\\\x22gb_1d gb_3a gb_Pd\\\\x22 data-ogsr-up\\\\x3d\\\\x22\\\\x22\\\\\\\n        x3e\\\\x3cdiv class\\\\x3d\\\\x22gb_We\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22gb_Xc\\\\\\\n        x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22gb_F gb_ld gb_l gb_Ff\\\\x22 data-ogsr-fb\\\\\\\n        x3d\\\\x22true\\\\x22 data-ogsr-alt\\\\x3d\\\\x22\\\\x22 id\\\\x3d\\\\x22gbwa\\\\x22\\\\x3e\\\\\\\n        x3cdiv class\\\\x3d\\\\x22gb_Ef\\\\x22\\\\x3e\\\\x3ca class\\\\x3d\\\\x22gb_d\\\\x22 aria-label\\\\\\\n        x3d\\\\x22Aplikasi Google\\\\x22 href\\\\x3d\\\\x22https://www.google.co.id/intl/id/about/products?tab\\\\\\\n        x3dwh\\\\x22 aria-expanded\\\\x3d\\\\x22false\\\\x22 role\\\\x3d\\\\x22button\\\\x22 tabindex\\\\\\\n        x3d\\\\x220\\\\x22\\\\x3e\\\\x3csvg class\\\\x3d\\\\x22gb_0e\\\\x22 focusable\\\\x3d\\\\x22false\\\\\\\n        x22 viewbox\\\\x3d\\\\x220 0 24 24\\\\x22\\\\x3e\\\\x3cpath d\\\\x3d\\\\x22M6,8c1.1,0 2,-0.9\\\n        \\ 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,20c1.1,0 2,-0.9 2,-2s-0.9,-2\\\n        \\ -2,-2 -2,0.9 -2,2 0.9,2 2,2zM6,20c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9\\\n        \\ -2,2 0.9,2 2,2zM6,14c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,14c1.1,0\\\n        \\ 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM16,6c0,1.1 0.9,2 2,2s2,-0.9\\\n        \\ 2,-2 -0.9,-2 -2,-2 -2,0.9 -2,2zM12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9\\\n        \\ -2,2 0.9,2 2,2zM18,14c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2\\\n        \\ 2,2zM18,20c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2z\\\\x22\\\\\\\n        x3e\\\\x3c/path\\\\x3e\\\\x3c/svg\\\\x3e\\\\x3c/a\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\\\\n        x3c/div\\\\x3e\\\\x3ca class\\\\x3d\\\\x22gb_7 gb_8 gb_de gb_dd\\\\x22 href\\\\x3d\\\\x22https://accounts.google.com/ServiceLogin?hl\\\\\\\n        x3did\\\\x26amp;passive\\\\x3dtrue\\\\x26amp;continue\\\\x3dhttps://www.google.ru/search%3Fnewwindow%3D1%26safe%3Doff%26q%3Dxkbcomp%2Balt%2Bgr%26oq%3Dxkbcomp%2Balt%2Bgr%26gs_l%3Dserp.3..33i21.28976559.28977886.0.28978017.6.6.0.0.0.0.167.668.0j5.5.0....0...1c.1.64.serp..1.2.311.06cSKPTLo18\\\\\\\n        x26amp;ec\\\\x3dGAZAAQ\\\\x22 target\\\\x3d\\\\x22_top\\\\x22\\\\x3eLogin\\\\x3c/a\\\\x3e\\\\\\\n        x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e');})();(function(){window.jsl.dh('_QyS5Y_SSL5HO5OUPp_mggAY_9','\\\\\\\n        x3cg-menu jsname\\\\x3d\\\\x22xl07Ob\\\\x22 class\\\\x3d\\\\x22cF4V5c zriOQb UU8UAb\\\n        \\ gLSAk rShyOb\\\\x22 jscontroller\\\\x3d\\\\x22WlNQGd\\\\x22 role\\\\x3d\\\\x22menu\\\\\\\n        x22 tabindex\\\\x3d\\\\x22-1\\\\x22 jsaction\\\\x3d\\\\x22PSl28c;focus:h06R8;keydown:uYT2Vb;mouseenter:WOQqYb;mouseleave:Tx5Rb;mouseover:IgJl9c\\\\\\\n        x22\\\\x3e\\\\x3cg-menu-item jsname\\\\x3d\\\\x22NNJLud\\\\x22 jscontroller\\\\x3d\\\\x22CnSW2d\\\\\\\n        x22 class\\\\x3d\\\\x22ErsxPb\\\\x22 role\\\\x3d\\\\x22none\\\\x22 data-short-label\\\\\\\n        x3d\\\\x22\\\\x22 jsdata\\\\x3d\\\\x22zPXzie;_;CQYHxw\\\\x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\\\\n        x22ibnC6b\\\\x22 class\\\\x3d\\\\x22znKVS OSrXXb tnhqA\\\\x22 role\\\\x3d\\\\x22none\\\\\\\n        x22\\\\x3e\\\\x3ca href\\\\x3d\\\\x22https://maps.google.ru/maps?newwindow\\\\x3d1\\\\\\\n        x26amp;safe\\\\x3doff\\\\x26amp;q\\\\x3dxkbcomp+alt+gr\\\\x26amp;um\\\\x3d1\\\\x26amp;ie\\\\\\\n        x3dUTF-8\\\\x26amp;sa\\\\x3dX\\\\x26amp;ved\\\\x3d2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ_AUoAHoECAEQCg\\\\\\\n        x22 role\\\\x3d\\\\x22menuitem\\\\x22 tabindex\\\\x3d\\\\x22-1\\\\x22\\\\x3e\\\\x3cspan class\\\\\\\n        x3d\\\\x22bmaJhd iJddsb\\\\x22 style\\\\x3d\\\\x22height:16px;width:16px\\\\x22\\\\x3e\\\\\\\n        x3csvg focusable\\\\x3d\\\\x22false\\\\x22 viewbox\\\\x3d\\\\x220 0 16 16\\\\x22\\\\x3e\\\\\\\n        x3cpath d\\\\x3d\\\\x22M7.503 0c3.09 0 5.502 2.487 5.502 5.427 0 2.337-1.13 3.694-2.26\\\n        \\ 5.05-.454.528-.906 1.13-1.358 1.734-.452.603-.754 1.508-.98 1.96-.226.452-.377.829-.904.829-.528\\\n        \\ 0-.678-.377-.905-.83-.226-.451-.527-1.356-.98-1.959-.452-.603-.904-1.206-1.356-1.734C3.132\\\n        \\ 9.121 2 7.764 2 5.427 2 2.487 4.412 0 7.503 0zm0 1.364c-2.283 0-4.14 1.822-4.14\\\n        \\ 4.063 0 1.843.86 2.873 1.946 4.177.468.547.942 1.178 1.4 1.79.34.452.596.99.794\\\n        \\ 1.444.198-.455.453-.992.793-1.445.459-.61.931-1.242 1.413-1.803 1.074-1.29\\\n        \\ 1.933-2.32 1.933-4.163 0-2.24-1.858-4.063-4.139-4.063zm0 2.734a1.33 1.33\\\n        \\ 0 11-.001 2.658 1.33 1.33 0 010-2.658\\\\x22\\\\x3e\\\\x3c/path\\\\x3e\\\\x3c/svg\\\\\\\n        x3e\\\\x3c/span\\\\x3eMaps\\\\x3c/a\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/g-menu-item\\\\x3e\\\\x3cg-menu-item\\\n        \\ jsname\\\\x3d\\\\x22NNJLud\\\\x22 jscontroller\\\\x3d\\\\x22CnSW2d\\\\x22 class\\\\x3d\\\\\\\n        x22ErsxPb\\\\x22 role\\\\x3d\\\\x22none\\\\x22 data-short-label\\\\x3d\\\\x22\\\\x22 jsdata\\\\\\\n        x3d\\\\x22zPXzie;_;CQYHxw\\\\x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22ibnC6b\\\\x22 class\\\\\\\n        x3d\\\\x22znKVS OSrXXb tnhqA\\\\x22 role\\\\x3d\\\\x22none\\\\x22\\\\x3e\\\\x3ca href\\\\\\\n        x3d\\\\x22/search?q\\\\x3dxkbcomp+alt+gr\\\\x26amp;newwindow\\\\x3d1\\\\x26amp;safe\\\\\\\n        x3doff\\\\x26amp;source\\\\x3dlnms\\\\x26amp;tbm\\\\x3dbks\\\\x26amp;sa\\\\x3dX\\\\x26amp;ved\\\\\\\n        x3d2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ_AUoAXoECAEQCw\\\\x22 role\\\\x3d\\\\x22menuitem\\\\\\\n        x22 tabindex\\\\x3d\\\\x22-1\\\\x22\\\\x3e\\\\x3cspan class\\\\x3d\\\\x22bmaJhd iJddsb\\\\\\\n        x22 style\\\\x3d\\\\x22height:16px;width:16px\\\\x22\\\\x3e\\\\x3csvg focusable\\\\x3d\\\\\\\n        x22false\\\\x22 viewbox\\\\x3d\\\\x220 0 24 24\\\\x22\\\\x3e\\\\x3cpath d\\\\x3d\\\\x22M18\\\n        \\ 2H6a2 2 0 0 0-2 2v16c0 1.1.9 2 2 2h12a2 2 0 0 0 2-2V4a2 2 0 0 0-2-2zm0 18H6V4h2v8l2.5-1.5L13\\\n        \\ 12V4h5v16\\\\x22\\\\x3e\\\\x3c/path\\\\x3e\\\\x3c/svg\\\\x3e\\\\x3c/span\\\\x3eBuku\\\\x3c/a\\\\\\\n        x3e\\\\x3c/div\\\\x3e\\\\x3c/g-menu-item\\\\x3e\\\\x3cg-menu-item jsname\\\\x3d\\\\x22NNJLud\\\\\\\n        x22 jscontroller\\\\x3d\\\\x22CnSW2d\\\\x22 class\\\\x3d\\\\x22ErsxPb\\\\x22 role\\\\x3d\\\\\\\n        x22none\\\\x22 data-short-label\\\\x3d\\\\x22\\\\x22 jsdata\\\\x3d\\\\x22zPXzie;_;CQYHxw\\\\\\\n        x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22ibnC6b\\\\x22 class\\\\x3d\\\\x22znKVS OSrXXb tnhqA\\\\\\\n        x22 role\\\\x3d\\\\x22none\\\\x22\\\\x3e\\\\x3ca href\\\\x3d\\\\x22https://www.google.ru/flights?q\\\\\\\n        x3dxkbcomp+alt+gr\\\\x26amp;newwindow\\\\x3d1\\\\x26amp;safe\\\\x3doff\\\\x26amp;source\\\\\\\n        x3dlnms\\\\x26amp;tbm\\\\x3dflm\\\\x26amp;sa\\\\x3dX\\\\x26amp;ved\\\\x3d2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ_AUoAnoECAEQDA\\\\\\\n        x22 role\\\\x3d\\\\x22menuitem\\\\x22 tabindex\\\\x3d\\\\x22-1\\\\x22\\\\x3e\\\\x3cspan class\\\\\\\n        x3d\\\\x22bmaJhd iJddsb\\\\x22 style\\\\x3d\\\\x22height:16px;width:16px\\\\x22\\\\x3e\\\\\\\n        x3csvg focusable\\\\x3d\\\\x22false\\\\x22 viewbox\\\\x3d\\\\x220 0 24 24\\\\x22\\\\x3e\\\\\\\n        x3cpath d\\\\x3d\\\\x22M12.98 12.89l-4.03 4.03.42 2.95L8.24 21l-1.87-3.37L3 15.76l1.12-1.12\\\n        \\ 2.95.42 4.03-4.03L3 6.77l1.5-1.5 10.04 2.32 4.2-4.2a1.32 1.32 0 0 1 1.87\\\n        \\ 0c.52.52.52 1.36 0 1.87l-4.2 4.2 2.32 10.04-1.5 1.5-4.25-8.11\\\\x22\\\\x3e\\\\\\\n        x3c/path\\\\x3e\\\\x3c/svg\\\\x3e\\\\x3c/span\\\\x3ePenerbangan\\\\x3c/a\\\\x3e\\\\x3c/div\\\\\\\n        x3e\\\\x3c/g-menu-item\\\\x3e\\\\x3cg-menu-item jsname\\\\x3d\\\\x22NNJLud\\\\x22 jscontroller\\\\\\\n        x3d\\\\x22CnSW2d\\\\x22 class\\\\x3d\\\\x22ErsxPb\\\\x22 role\\\\x3d\\\\x22none\\\\x22 data-short-label\\\\\\\n        x3d\\\\x22\\\\x22 jsdata\\\\x3d\\\\x22zPXzie;_;CQYHxw\\\\x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\\\\n        x22ibnC6b\\\\x22 class\\\\x3d\\\\x22znKVS OSrXXb tnhqA\\\\x22 role\\\\x3d\\\\x22none\\\\\\\n        x22\\\\x3e\\\\x3ca href\\\\x3d\\\\x22https://www.google.com/finance?sa\\\\x3dX\\\\x26amp;ved\\\\\\\n        x3d2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ_AUoA3oECAEQDQ\\\\x22 role\\\\x3d\\\\x22menuitem\\\\\\\n        x22 tabindex\\\\x3d\\\\x22-1\\\\x22\\\\x3e\\\\x3cspan class\\\\x3d\\\\x22bmaJhd iJddsb\\\\\\\n        x22 style\\\\x3d\\\\x22height:16px;width:16px\\\\x22\\\\x3e\\\\x3csvg focusable\\\\x3d\\\\\\\n        x22false\\\\x22 viewbox\\\\x3d\\\\x220 0 24 24\\\\x22\\\\x3e\\\\x3cpath d\\\\x3d\\\\x22M6\\\n        \\ 15.5l-3 2.94V10h3v5.5zm5-1.84l-1.57-1.34L8 13.64V6h3v7.66zM16 12l-3 3V2h3v10zm2.81-.19L17\\\n        \\ 10h5v5l-1.79-1.79L13 20.36l-3.47-3.02L5.75 21H3l6.47-6.34L13 17.64l5.81-5.83\\\\\\\n        x22\\\\x3e\\\\x3c/path\\\\x3e\\\\x3c/svg\\\\x3e\\\\x3c/span\\\\x3eKeuangan\\\\x3c/a\\\\x3e\\\\\\\n        x3c/div\\\\x3e\\\\x3c/g-menu-item\\\\x3e\\\\x3c/g-menu\\\\x3e');})();(function(){window.jsl.dh('tn_1','\\\\\\\n        x3cdiv class\\\\x3d\\\\x22LkcePc\\\\x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cspan jscontroller\\\\\\\n        x3d\\\\x22nabPbb\\\\x22 jsaction\\\\x3d\\\\x22KyPa0e:Y0y4c;BVfjhf:VFzweb;wjOG7e:gDkf4c;\\\\\\\n        x22\\\\x3e\\\\x3cg-popup jsname\\\\x3d\\\\x22V68bde\\\\x22 jscontroller\\\\x3d\\\\x22DPreE\\\\\\\n        x22 jsaction\\\\x3d\\\\x22A05xBd:IYtByb;EOZ57e:WFrRFb;\\\\x22 jsdata\\\\x3d\\\\x22mVjAjf;_;CQYHxs\\\\\\\n        x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22oYxtQd\\\\x22 class\\\\x3d\\\\x22rIbAWc\\\\x22 aria-expanded\\\\\\\n        x3d\\\\x22false\\\\x22 aria-haspopup\\\\x3d\\\\x22true\\\\x22 role\\\\x3d\\\\x22button\\\\\\\n        x22 tabindex\\\\x3d\\\\x220\\\\x22 jsaction\\\\x3d\\\\x22WFrRFb;keydown:uYT2Vb\\\\x22\\\\\\\n        x3e\\\\x3cdiv jsname\\\\x3d\\\\x22LgbsSe\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22hdtb-mn-hd\\\n        \\ Yg3U7e\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22KTBKoe\\\\x22\\\\x3eBahasa apa saja\\\\\\\n        x3c/div\\\\x3e\\\\x3cspan class\\\\x3d\\\\x22gTl8xb\\\\x22\\\\x3e\\\\x3c/span\\\\x3e\\\\x3c/div\\\\\\\n        x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22V68bde\\\\x22 class\\\\\\\n        x3d\\\\x22EwsJzb sAKBe B8Kd8d\\\\x22 style\\\\x3d\\\\x22display:none;z-index:200\\\\\\\n        x22\\\\x3e\\\\x3cg-menu jsname\\\\x3d\\\\x22xl07Ob\\\\x22 class\\\\x3d\\\\x22cF4V5c Tlae9d\\\n        \\ gLSAk rShyOb\\\\x22 jscontroller\\\\x3d\\\\x22WlNQGd\\\\x22 role\\\\x3d\\\\x22menu\\\\\\\n        x22 tabindex\\\\x3d\\\\x22-1\\\\x22 jsaction\\\\x3d\\\\x22PSl28c;focus:h06R8;keydown:uYT2Vb;mouseenter:WOQqYb;mouseleave:Tx5Rb;mouseover:IgJl9c\\\\\\\n        x22\\\\x3e\\\\x3cg-menu-item jsname\\\\x3d\\\\x22NNJLud\\\\x22 class\\\\x3d\\\\x22nvELY\\\n        \\ ErsxPb\\\\x22 jscontroller\\\\x3d\\\\x22CnSW2d\\\\x22 role\\\\x3d\\\\x22none\\\\x22 data-short-label\\\\\\\n        x3d\\\\x22\\\\x22 jsdata\\\\x3d\\\\x22zPXzie;_;CQYHxw\\\\x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\\\\n        x22ibnC6b\\\\x22 class\\\\x3d\\\\x22znKVS OSrXXb tnhqA\\\\x22 role\\\\x3d\\\\x22none\\\\\\\n        x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22y0fQ9c\\\\x22 aria-checked\\\\x3d\\\\x22true\\\\x22\\\n        \\ role\\\\x3d\\\\x22menuitemradio\\\\x22 tabindex\\\\x3d\\\\x220\\\\x22\\\\x3eBahasa apa\\\n        \\ saja\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/g-menu-item\\\\x3e\\\\x3cg-menu-item jsname\\\\\\\n        x3d\\\\x22NNJLud\\\\x22 jscontroller\\\\x3d\\\\x22CnSW2d\\\\x22 class\\\\x3d\\\\x22ErsxPb\\\\\\\n        x22 role\\\\x3d\\\\x22none\\\\x22 data-short-label\\\\x3d\\\\x22\\\\x22 jsdata\\\\x3d\\\\\\\n        x22zPXzie;_;CQYHxw\\\\x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22ibnC6b\\\\x22 class\\\\x3d\\\\\\\n        x22znKVS OSrXXb tnhqA\\\\x22 role\\\\x3d\\\\x22none\\\\x22\\\\x3e\\\\x3ca href\\\\x3d\\\\\\\n        x22/search?q\\\\x3dxkbcomp+alt+gr\\\\x26amp;newwindow\\\\x3d1\\\\x26amp;safe\\\\x3doff\\\\\\\n        x26amp;source\\\\x3dlnt\\\\x26amp;tbs\\\\x3dlr:lang_1id\\\\x26amp;lr\\\\x3dlang_id\\\\\\\n        x26amp;sa\\\\x3dX\\\\x26amp;ved\\\\x3d2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQpwV6BAgBEBU\\\\\\\n        x22 aria-checked\\\\x3d\\\\x22false\\\\x22 role\\\\x3d\\\\x22menuitemradio\\\\x22\\\\x3eTelusuri\\\n        \\ halaman berbahasa Indonesia\\\\x3c/a\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/g-menu-item\\\\\\\n        x3e\\\\x3c/g-menu\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/g-popup\\\\x3e\\\\x3c/span\\\\x3e\\\\x3cspan\\\n        \\ jscontroller\\\\x3d\\\\x22nabPbb\\\\x22 jsaction\\\\x3d\\\\x22KyPa0e:Y0y4c;BVfjhf:VFzweb;wjOG7e:gDkf4c;\\\\\\\n        x22\\\\x3e\\\\x3cg-popup jsname\\\\x3d\\\\x22V68bde\\\\x22 jscontroller\\\\x3d\\\\x22DPreE\\\\\\\n        x22 jsaction\\\\x3d\\\\x22A05xBd:IYtByb;EOZ57e:WFrRFb;\\\\x22 jsdata\\\\x3d\\\\x22mVjAjf;_;CQYHxs\\\\\\\n        x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22oYxtQd\\\\x22 class\\\\x3d\\\\x22rIbAWc\\\\x22 aria-expanded\\\\\\\n        x3d\\\\x22false\\\\x22 aria-haspopup\\\\x3d\\\\x22true\\\\x22 role\\\\x3d\\\\x22button\\\\\\\n        x22 tabindex\\\\x3d\\\\x220\\\\x22 jsaction\\\\x3d\\\\x22WFrRFb;keydown:uYT2Vb\\\\x22\\\\\\\n        x3e\\\\x3cdiv jsname\\\\x3d\\\\x22LgbsSe\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22hdtb-mn-hd\\\\\\\n        x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22KTBKoe\\\\x22\\\\x3eSembarang waktu\\\\x3c/div\\\\\\\n        x3e\\\\x3cspan class\\\\x3d\\\\x22gTl8xb\\\\x22\\\\x3e\\\\x3c/span\\\\x3e\\\\x3c/div\\\\x3e\\\\\\\n        x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22V68bde\\\\x22 class\\\\x3d\\\\\\\n        x22EwsJzb sAKBe B8Kd8d\\\\x22 style\\\\x3d\\\\x22display:none;z-index:200\\\\x22\\\\\\\n        x3e\\\\x3cg-menu jsname\\\\x3d\\\\x22xl07Ob\\\\x22 class\\\\x3d\\\\x22cF4V5c Tlae9d gLSAk\\\n        \\ rShyOb\\\\x22 jscontroller\\\\x3d\\\\x22WlNQGd\\\\x22 role\\\\x3d\\\\x22menu\\\\x22 tabindex\\\\\\\n        x3d\\\\x22-1\\\\x22 jsaction\\\\x3d\\\\x22PSl28c;focus:h06R8;keydown:uYT2Vb;mouseenter:WOQqYb;mouseleave:Tx5Rb;mouseover:IgJl9c\\\\\\\n        x22\\\\x3e\\\\x3cg-menu-item jsname\\\\x3d\\\\x22NNJLud\\\\x22 class\\\\x3d\\\\x22nvELY\\\n        \\ ErsxPb\\\\x22 jscontroller\\\\x3d\\\\x22CnSW2d\\\\x22 role\\\\x3d\\\\x22none\\\\x22 data-short-label\\\\\\\n        x3d\\\\x22\\\\x22 jsdata\\\\x3d\\\\x22zPXzie;_;CQYHxw\\\\x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\\\\n        x22ibnC6b\\\\x22 class\\\\x3d\\\\x22znKVS OSrXXb tnhqA\\\\x22 role\\\\x3d\\\\x22none\\\\\\\n        x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22y0fQ9c\\\\x22 aria-checked\\\\x3d\\\\x22true\\\\x22\\\n        \\ role\\\\x3d\\\\x22menuitemradio\\\\x22 tabindex\\\\x3d\\\\x220\\\\x22\\\\x3eSembarang\\\n        \\ waktu\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/g-menu-item\\\\x3e\\\\x3cg-menu-item\\\n        \\ jsname\\\\x3d\\\\x22NNJLud\\\\x22 jscontroller\\\\x3d\\\\x22CnSW2d\\\\x22 class\\\\x3d\\\\\\\n        x22ErsxPb\\\\x22 role\\\\x3d\\\\x22none\\\\x22 data-short-label\\\\x3d\\\\x22\\\\x22 jsdata\\\\\\\n        x3d\\\\x22zPXzie;_;CQYHxw\\\\x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22ibnC6b\\\\x22 class\\\\\\\n        x3d\\\\x22znKVS OSrXXb tnhqA\\\\x22 role\\\\x3d\\\\x22none\\\\x22\\\\x3e\\\\x3ca href\\\\\\\n        x3d\\\\x22/search?q\\\\x3dxkbcomp+alt+gr\\\\x26amp;newwindow\\\\x3d1\\\\x26amp;safe\\\\\\\n        x3doff\\\\x26amp;source\\\\x3dlnt\\\\x26amp;tbs\\\\x3dqdr:h\\\\x26amp;sa\\\\x3dX\\\\x26amp;ved\\\\\\\n        x3d2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQpwV6BAgBEBo\\\\x22 aria-checked\\\\x3d\\\\x22false\\\\\\\n        x22 role\\\\x3d\\\\x22menuitemradio\\\\x22\\\\x3e 1 jam terakhir\\\\x3c/a\\\\x3e\\\\x3c/div\\\\\\\n        x3e\\\\x3c/g-menu-item\\\\x3e\\\\x3cg-menu-item jsname\\\\x3d\\\\x22NNJLud\\\\x22 jscontroller\\\\\\\n        x3d\\\\x22CnSW2d\\\\x22 class\\\\x3d\\\\x22ErsxPb\\\\x22 role\\\\x3d\\\\x22none\\\\x22 data-short-label\\\\\\\n        x3d\\\\x22\\\\x22 jsdata\\\\x3d\\\\x22zPXzie;_;CQYHxw\\\\x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\\\\n        x22ibnC6b\\\\x22 class\\\\x3d\\\\x22znKVS OSrXXb tnhqA\\\\x22 role\\\\x3d\\\\x22none\\\\\\\n        x22\\\\x3e\\\\x3ca href\\\\x3d\\\\x22/search?q\\\\x3dxkbcomp+alt+gr\\\\x26amp;newwindow\\\\\\\n        x3d1\\\\x26amp;safe\\\\x3doff\\\\x26amp;source\\\\x3dlnt\\\\x26amp;tbs\\\\x3dqdr:d\\\\x26amp;sa\\\\\\\n        x3dX\\\\x26amp;ved\\\\x3d2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQpwV6BAgBEBs\\\\x22 aria-checked\\\\\\\n        x3d\\\\x22false\\\\x22 role\\\\x3d\\\\x22menuitemradio\\\\x22\\\\x3e 24 jam terakhir\\\\\\\n        x3c/a\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/g-menu-item\\\\x3e\\\\x3cg-menu-item jsname\\\\x3d\\\\\\\n        x22NNJLud\\\\x22 jscontroller\\\\x3d\\\\x22CnSW2d\\\\x22 class\\\\x3d\\\\x22ErsxPb\\\\x22\\\n        \\ role\\\\x3d\\\\x22none\\\\x22 data-short-label\\\\x3d\\\\x22\\\\x22 jsdata\\\\x3d\\\\x22zPXzie;_;CQYHxw\\\\\\\n        x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22ibnC6b\\\\x22 class\\\\x3d\\\\x22znKVS OSrXXb tnhqA\\\\\\\n        x22 role\\\\x3d\\\\x22none\\\\x22\\\\x3e\\\\x3ca href\\\\x3d\\\\x22/search?q\\\\x3dxkbcomp+alt+gr\\\\\\\n        x26amp;newwindow\\\\x3d1\\\\x26amp;safe\\\\x3doff\\\\x26amp;source\\\\x3dlnt\\\\x26amp;tbs\\\\\\\n        x3dqdr:w\\\\x26amp;sa\\\\x3dX\\\\x26amp;ved\\\\x3d2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQpwV6BAgBEBw\\\\\\\n        x22 aria-checked\\\\x3d\\\\x22false\\\\x22 role\\\\x3d\\\\x22menuitemradio\\\\x22\\\\x3e\\\n        \\ Seminggu terakhir\\\\x3c/a\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/g-menu-item\\\\x3e\\\\x3cg-menu-item\\\n        \\ jsname\\\\x3d\\\\x22NNJLud\\\\x22 jscontroller\\\\x3d\\\\x22CnSW2d\\\\x22 class\\\\x3d\\\\\\\n        x22ErsxPb\\\\x22 role\\\\x3d\\\\x22none\\\\x22 data-short-label\\\\x3d\\\\x22\\\\x22 jsdata\\\\\\\n        x3d\\\\x22zPXzie;_;CQYHxw\\\\x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22ibnC6b\\\\x22 class\\\\\\\n        x3d\\\\x22znKVS OSrXXb tnhqA\\\\x22 role\\\\x3d\\\\x22none\\\\x22\\\\x3e\\\\x3ca href\\\\\\\n        x3d\\\\x22/search?q\\\\x3dxkbcomp+alt+gr\\\\x26amp;newwindow\\\\x3d1\\\\x26amp;safe\\\\\\\n        x3doff\\\\x26amp;source\\\\x3dlnt\\\\x26amp;tbs\\\\x3dqdr:m\\\\x26amp;sa\\\\x3dX\\\\x26amp;ved\\\\\\\n        x3d2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQpwV6BAgBEB0\\\\x22 aria-checked\\\\x3d\\\\x22false\\\\\\\n        x22 role\\\\x3d\\\\x22menuitemradio\\\\x22\\\\x3e Sebulan terakhir\\\\x3c/a\\\\x3e\\\\x3c/div\\\\\\\n        x3e\\\\x3c/g-menu-item\\\\x3e\\\\x3cg-menu-item jsname\\\\x3d\\\\x22NNJLud\\\\x22 jscontroller\\\\\\\n        x3d\\\\x22CnSW2d\\\\x22 class\\\\x3d\\\\x22ErsxPb\\\\x22 role\\\\x3d\\\\x22none\\\\x22 data-short-label\\\\\\\n        x3d\\\\x22\\\\x22 jsdata\\\\x3d\\\\x22zPXzie;_;CQYHxw\\\\x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\\\\n        x22ibnC6b\\\\x22 class\\\\x3d\\\\x22znKVS OSrXXb tnhqA\\\\x22 role\\\\x3d\\\\x22none\\\\\\\n        x22\\\\x3e\\\\x3ca href\\\\x3d\\\\x22/search?q\\\\x3dxkbcomp+alt+gr\\\\x26amp;newwindow\\\\\\\n        x3d1\\\\x26amp;safe\\\\x3doff\\\\x26amp;source\\\\x3dlnt\\\\x26amp;tbs\\\\x3dqdr:y\\\\x26amp;sa\\\\\\\n        x3dX\\\\x26amp;ved\\\\x3d2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQpwV6BAgBEB4\\\\x22 aria-checked\\\\\\\n        x3d\\\\x22false\\\\x22 role\\\\x3d\\\\x22menuitemradio\\\\x22\\\\x3e Setahun terakhir\\\\\\\n        x3c/a\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/g-menu-item\\\\x3e\\\\x3cg-menu-item jsname\\\\x3d\\\\\\\n        x22NNJLud\\\\x22 jscontroller\\\\x3d\\\\x22CnSW2d\\\\x22 class\\\\x3d\\\\x22ErsxPb\\\\x22\\\n        \\ role\\\\x3d\\\\x22none\\\\x22 data-short-label\\\\x3d\\\\x22\\\\x22 jsdata\\\\x3d\\\\x22zPXzie;_;CQYHxw\\\\\\\n        x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22ibnC6b\\\\x22 class\\\\x3d\\\\x22znKVS OSrXXb tnhqA\\\\\\\n        x22 role\\\\x3d\\\\x22none\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22y0fQ9c\\\\x22 jscontroller\\\\\\\n        x3d\\\\x22VD4Qme\\\\x22 data-m\\\\x3d\\\\x22false\\\\x22\\\\x3e\\\\x3cspan role\\\\x3d\\\\x22menuitem\\\\\\\n        x22 tabindex\\\\x3d\\\\x22-1\\\\x22 jsaction\\\\x3d\\\\x22EEGHee\\\\x22 data-ved\\\\x3d\\\\\\\n        x222ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQpwV6BAgBEB8\\\\x22\\\\x3eRentang tertentu...\\\\\\\n        x3c/span\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22n5Ug4b\\\\x22 style\\\\x3d\\\\x22display:none\\\\\\\n        x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22vOvh1b\\\\x22 jsaction\\\\x3d\\\\x22xp3IKd\\\\x22\\\\\\\n        x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22J6UZg\\\\x22 aria-modal\\\\x3d\\\\x22true\\\\\\\n        x22 role\\\\x3d\\\\x22dialog\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22Jy9Ore\\\\x22 role\\\\\\\n        x3d\\\\x22heading\\\\x22\\\\x3eRentang tanggal khusus\\\\x3c/div\\\\x3e\\\\x3clabel class\\\\\\\n        x3d\\\\x22Qtsmnf tmDYm\\\\x22 for\\\\x3d\\\\x22OouJcb\\\\x22\\\\x3eDari\\\\x3c/label\\\\x3e\\\\\\\n        x3clabel class\\\\x3d\\\\x22Qtsmnf iWBKNe\\\\x22 for\\\\x3d\\\\x22rzG2be\\\\x22\\\\x3eSampai\\\\\\\n        x3c/label\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22Gwgzqd\\\\x22 aria-label\\\\x3d\\\\x22Tutup\\\\\\\n        x22 role\\\\x3d\\\\x22button\\\\x22 tabindex\\\\x3d\\\\x220\\\\x22 jsaction\\\\x3d\\\\x22xp3IKd\\\\\\\n        x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22NwEGxd\\\\x22\\\\x3e\\\\x3cdiv class\\\\\\\n        x3d\\\\x22qomYCd\\\\x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cform action\\\\x3d\\\\x22/search\\\\\\\n        x22 class\\\\x3d\\\\x22T3kYXe\\\\x22 id\\\\x3d\\\\x22T3kYXe\\\\x22 method\\\\x3d\\\\x22get\\\\\\\n        x22\\\\x3e\\\\x3cinput name\\\\x3d\\\\x22q\\\\x22 value\\\\x3d\\\\x22xkbcomp alt gr\\\\x22\\\n        \\ type\\\\x3d\\\\x22hidden\\\\x22\\\\x3e\\\\x3cinput name\\\\x3d\\\\x22newwindow\\\\x22 value\\\\\\\n        x3d\\\\x221\\\\x22 type\\\\x3d\\\\x22hidden\\\\x22\\\\x3e\\\\x3cinput name\\\\x3d\\\\x22safe\\\\\\\n        x22 value\\\\x3d\\\\x22off\\\\x22 type\\\\x3d\\\\x22hidden\\\\x22\\\\x3e\\\\x3cinput name\\\\\\\n        x3d\\\\x22source\\\\x22 type\\\\x3d\\\\x22hidden\\\\x22 value\\\\x3d\\\\x22lnt\\\\x22\\\\x3e\\\\\\\n        x3cinput value\\\\x3d\\\\x22cdr:1,cd_min:x,cd_max:x\\\\x22 id\\\\x3d\\\\x22HjtPBb\\\\\\\n        x22 name\\\\x3d\\\\x22tbs\\\\x22 type\\\\x3d\\\\x22hidden\\\\x22\\\\x3e\\\\x3cinput value\\\\\\\n        x3d\\\\x22\\\\x22 name\\\\x3d\\\\x22tbm\\\\x22 type\\\\x3d\\\\x22hidden\\\\x22\\\\x3e\\\\x3cinput\\\n        \\ class\\\\x3d\\\\x22OouJcb\\\\x22 type\\\\x3d\\\\x22text\\\\x22 value\\\\x3d\\\\x22\\\\x22\\\n        \\ autocomplete\\\\x3d\\\\x22off\\\\x22 id\\\\x3d\\\\x22OouJcb\\\\x22 jsaction\\\\x3d\\\\x22focus:daRB0b\\\\\\\n        x22\\\\x3e\\\\x3cinput class\\\\x3d\\\\x22rzG2be\\\\x22 type\\\\x3d\\\\x22text\\\\x22 value\\\\\\\n        x3d\\\\x22\\\\x22 autocomplete\\\\x3d\\\\x22off\\\\x22 id\\\\x3d\\\\x22rzG2be\\\\x22 jsaction\\\\\\\n        x3d\\\\x22focus:daRB0b\\\\x22\\\\x3e\\\\x3cg-button class\\\\x3d\\\\x22Ru1Ao BwGU8e fE5Rge\\\\\\\n        x22 jsaction\\\\x3d\\\\x22hNEEAb\\\\x22 role\\\\x3d\\\\x22button\\\\x22 tabindex\\\\x3d\\\\\\\n        x220\\\\x22\\\\x3eMulai\\\\x3c/g-button\\\\x3e\\\\x3cinput type\\\\x3d\\\\x22submit\\\\x22\\\n        \\ jsaction\\\\x3d\\\\x22zbvklb\\\\x22 style\\\\x3d\\\\x22display:none\\\\x22\\\\x3e\\\\x3c/form\\\\\\\n        x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\\\\n        x3c/g-menu-item\\\\x3e\\\\x3c/g-menu\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/g-popup\\\\x3e\\\\x3c/span\\\\\\\n        x3e\\\\x3cspan jscontroller\\\\x3d\\\\x22nabPbb\\\\x22 jsaction\\\\x3d\\\\x22KyPa0e:Y0y4c;BVfjhf:VFzweb;wjOG7e:gDkf4c;\\\\\\\n        x22\\\\x3e\\\\x3cg-popup jsname\\\\x3d\\\\x22V68bde\\\\x22 jscontroller\\\\x3d\\\\x22DPreE\\\\\\\n        x22 jsaction\\\\x3d\\\\x22A05xBd:IYtByb;EOZ57e:WFrRFb;\\\\x22 jsdata\\\\x3d\\\\x22mVjAjf;_;CQYHxs\\\\\\\n        x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22oYxtQd\\\\x22 class\\\\x3d\\\\x22rIbAWc\\\\x22 aria-expanded\\\\\\\n        x3d\\\\x22false\\\\x22 aria-haspopup\\\\x3d\\\\x22true\\\\x22 role\\\\x3d\\\\x22button\\\\\\\n        x22 tabindex\\\\x3d\\\\x220\\\\x22 jsaction\\\\x3d\\\\x22WFrRFb;keydown:uYT2Vb\\\\x22\\\\\\\n        x3e\\\\x3cdiv jsname\\\\x3d\\\\x22LgbsSe\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22hdtb-mn-hd\\\\\\\n        x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22KTBKoe\\\\x22\\\\x3eSemua hasil\\\\x3c/div\\\\x3e\\\\\\\n        x3cspan class\\\\x3d\\\\x22gTl8xb\\\\x22\\\\x3e\\\\x3c/span\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\\\\n        x3e\\\\x3c/div\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22V68bde\\\\x22 class\\\\x3d\\\\x22EwsJzb\\\n        \\ sAKBe B8Kd8d\\\\x22 style\\\\x3d\\\\x22display:none;z-index:200\\\\x22\\\\x3e\\\\x3cg-menu\\\n        \\ jsname\\\\x3d\\\\x22xl07Ob\\\\x22 class\\\\x3d\\\\x22cF4V5c Tlae9d gLSAk rShyOb\\\\\\\n        x22 jscontroller\\\\x3d\\\\x22WlNQGd\\\\x22 role\\\\x3d\\\\x22menu\\\\x22 tabindex\\\\x3d\\\\\\\n        x22-1\\\\x22 jsaction\\\\x3d\\\\x22PSl28c;focus:h06R8;keydown:uYT2Vb;mouseenter:WOQqYb;mouseleave:Tx5Rb;mouseover:IgJl9c\\\\\\\n        x22\\\\x3e\\\\x3cg-menu-item jsname\\\\x3d\\\\x22NNJLud\\\\x22 class\\\\x3d\\\\x22nvELY\\\n        \\ ErsxPb\\\\x22 jscontroller\\\\x3d\\\\x22CnSW2d\\\\x22 role\\\\x3d\\\\x22none\\\\x22 data-short-label\\\\\\\n        x3d\\\\x22\\\\x22 jsdata\\\\x3d\\\\x22zPXzie;_;CQYHxw\\\\x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\\\\n        x22ibnC6b\\\\x22 class\\\\x3d\\\\x22znKVS OSrXXb tnhqA\\\\x22 role\\\\x3d\\\\x22none\\\\\\\n        x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22y0fQ9c\\\\x22 aria-checked\\\\x3d\\\\x22true\\\\x22\\\n        \\ role\\\\x3d\\\\x22menuitemradio\\\\x22 tabindex\\\\x3d\\\\x220\\\\x22\\\\x3eSemua hasil\\\\\\\n        x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/g-menu-item\\\\x3e\\\\x3cg-menu-item jsname\\\\\\\n        x3d\\\\x22NNJLud\\\\x22 jscontroller\\\\x3d\\\\x22CnSW2d\\\\x22 class\\\\x3d\\\\x22ErsxPb\\\\\\\n        x22 role\\\\x3d\\\\x22none\\\\x22 data-short-label\\\\x3d\\\\x22\\\\x22 jsdata\\\\x3d\\\\\\\n        x22zPXzie;_;CQYHxw\\\\x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22ibnC6b\\\\x22 class\\\\x3d\\\\\\\n        x22znKVS OSrXXb tnhqA\\\\x22 role\\\\x3d\\\\x22none\\\\x22\\\\x3e\\\\x3ca href\\\\x3d\\\\\\\n        x22/search?q\\\\x3dxkbcomp+alt+gr\\\\x26amp;newwindow\\\\x3d1\\\\x26amp;safe\\\\x3doff\\\\\\\n        x26amp;source\\\\x3dlnt\\\\x26amp;tbs\\\\x3dli:1\\\\x26amp;sa\\\\x3dX\\\\x26amp;ved\\\\\\\n        x3d2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQpwV6BAgBECU\\\\x22 aria-checked\\\\x3d\\\\x22false\\\\\\\n        x22 role\\\\x3d\\\\x22menuitemradio\\\\x22\\\\x3eApa adanya\\\\x3c/a\\\\x3e\\\\x3c/div\\\\\\\n        x3e\\\\x3c/g-menu-item\\\\x3e\\\\x3c/g-menu\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/g-popup\\\\x3e\\\\\\\n        x3c/span\\\\x3e');})();(function(){window.jsl.dh('spic_1','\\\\x3cdiv jsname\\\\\\\n        x3d\\\\x22TItCJc\\\\x22 class\\\\x3d\\\\x22CbAZb\\\\x22 id\\\\x3d\\\\x22elPddd\\\\x22 role\\\\\\\n        x3d\\\\x22dialog\\\\x22 tabindex\\\\x3d\\\\x22-1\\\\x22 jsaction\\\\x3d\\\\x22mLt3mc\\\\x22\\\n        \\ data-ved\\\\x3d\\\\x222ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQzpwIegQICxAC\\\\x22\\\\x3e\\\\\\\n        x3cdiv class\\\\x3d\\\\x22cQ2awd\\\\x22\\\\x3e\\\\x3ch1 class\\\\x3d\\\\x22S8wJ3\\\\x22\\\\\\\n        x3eSetelan Cepat\\\\x3c/h1\\\\x3e\\\\x3cspan class\\\\x3d\\\\x22bepeF z1asCe wuXmqc\\\\\\\n        x22 aria-label\\\\x3d\\\\x22Close\\\\x22 role\\\\x3d\\\\x22button\\\\x22 tabindex\\\\x3d\\\\\\\n        x220\\\\x22 jsaction\\\\x3d\\\\x22UVNdjb\\\\x22 data-ved\\\\x3d\\\\x222ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQy9QJegQICxAD\\\\\\\n        x22\\\\x3e\\\\x3csvg focusable\\\\x3d\\\\x22false\\\\x22 xmlns\\\\x3d\\\\x22http://www.w3.org/2000/svg\\\\\\\n        x22 viewBox\\\\x3d\\\\x220 0 24 24\\\\x22\\\\x3e\\\\x3cpath d\\\\x3d\\\\x22M19 6.41L17.59\\\n        \\ 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59\\\n        \\ 13.41 12z\\\\x22\\\\x3e\\\\x3c/path\\\\x3e\\\\x3c/svg\\\\x3e\\\\x3c/span\\\\x3e\\\\x3ca class\\\\\\\n        x3d\\\\x22tGS0Nc\\\\x22 href\\\\x3d\\\\x22/preferences?hl\\\\x3did\\\\x26amp;prev\\\\x3dhttps://www.google.ru/search?newwindow%3D1%26safe%3Doff%26q%3Dxkbcomp%2Balt%2Bgr%26oq%3Dxkbcomp%2Balt%2Bgr%26gs_l%3Dserp.3..33i21.28976559.28977886.0.28978017.6.6.0.0.0.0.167.668.0j5.5.0....0...1c.1.64.serp..1.2.311.06cSKPTLo18\\\\\\\n        x22 tabindex\\\\x3d\\\\x220\\\\x22 data-jsarwt\\\\x3d\\\\x221\\\\x22 data-usg\\\\x3d\\\\x22AOvVaw2kad61sp5q8y1_BoHplmU-\\\\\\\n        x22 data-ved\\\\x3d\\\\x222ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ65sIegQICxAE\\\\x22\\\\\\\n        x3eLihat semua setelan Penelusuran\\\\x3c/a\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\\\\n        x3d\\\\x22m0uvVb\\\\x22\\\\x3e\\\\x3ch2 class\\\\x3d\\\\x22kwWBYc\\\\x22\\\\x3eAktivitas Anda\\\\\\\n        x3c/h2\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22q0yked\\\\x22\\\\x3e\\\\x3ca href\\\\x3d\\\\x22/history/optout?hl\\\\\\\n        x3did\\\\x22 data-jsarwt\\\\x3d\\\\x221\\\\x22 data-usg\\\\x3d\\\\x22AOvVaw3WsbBdQcQuhIw9weEaKtCc\\\\\\\n        x22 data-ved\\\\x3d\\\\x222ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQpdoJegQICxAF\\\\x22\\\\\\\n        x3e\\\\x3cspan class\\\\x3d\\\\x22ZI7elf\\\\x22\\\\x3ePenyesuaian Penelusuran\\\\x3c/span\\\\\\\n        x3e\\\\x3cdiv class\\\\x3d\\\\x22ogD9ue\\\\x22\\\\x3e\\\\x3cspan class\\\\x3d\\\\x22rhJQGd\\\n        \\ tkWDZc\\\\x22 data-ved\\\\x3d\\\\x222ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQp9oJegQICxAG\\\\\\\n        x22\\\\x3eNonaktif\\\\x3c/span\\\\x3e\\\\x3cspan style\\\\x3d\\\\x22color:#5f6368\\\\x22\\\n        \\ class\\\\x3d\\\\x22z1asCe eznrjd\\\\x22\\\\x3e\\\\x3csvg focusable\\\\x3d\\\\x22false\\\\\\\n        x22 xmlns\\\\x3d\\\\x22http://www.w3.org/2000/svg\\\\x22 viewBox\\\\x3d\\\\x220 0 24\\\n        \\ 24\\\\x22\\\\x3e\\\\x3cpath d\\\\x3d\\\\x22M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0\\\n        \\ 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19\\\n        \\ 6.41V10h2V3h-7z\\\\x22\\\\x3e\\\\x3c/path\\\\x3e\\\\x3c/svg\\\\x3e\\\\x3c/span\\\\x3e\\\\\\\n        x3c/div\\\\x3e\\\\x3c/a\\\\x3e\\\\x3c/div\\\\x3e\\\\x3ca class\\\\x3d\\\\x22SknMB\\\\x22 href\\\\\\\n        x3d\\\\x22/history/privacyadvisor/search/unauth?utm_source\\\\x3dgooglemenu\\\\\\\n        x22 data-jsarwt\\\\x3d\\\\x221\\\\x22 data-usg\\\\x3d\\\\x22AOvVaw3fGEMuCbvjydIC0F4ytIn8\\\\\\\n        x22 data-ved\\\\x3d\\\\x222ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ7ZsIegQICxAH\\\\x22\\\\\\\n        x3e\\\\x3cspan class\\\\x3d\\\\x22tkWDZc\\\\x22\\\\x3ePelajari lebih lanjut data Anda\\\n        \\ di Penelusuran\\\\x3c/span\\\\x3e\\\\x3c/a\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\\\\n        x3d\\\\x22m0uvVb\\\\x22\\\\x3e\\\\x3ch2 class\\\\x3d\\\\x22kwWBYc\\\\x22\\\\x3eMenggunakan\\\n        \\ Penelusuran\\\\x3c/h2\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22fmxhfc\\\\x22\\\\x3e\\\\x3cdiv\\\n        \\ jscontroller\\\\x3d\\\\x22GGTOgd\\\\x22 class\\\\x3d\\\\x22LWBVLb\\\\x22 data-safesearch-enabled\\\\\\\n        x3d\\\\x220\\\\x22 data-setprefs-off-url\\\\x3d\\\\x22/setprefs?authuser\\\\x3d\\\\x26amp;sig\\\\\\\n        x3d0_fI0GmKthH8jgM-1KWGWa9pya5ps%3D\\\\x26amp;prev\\\\x3dhttps://www.google.ru/search?newwindow%3D1%26safe%3Doff%26q%3Dxkbcomp%2Balt%2Bgr%26oq%3Dxkbcomp%2Balt%2Bgr%26gs_l%3Dserp.3..33i21.28976559.28977886.0.28978017.6.6.0.0.0.0.167.668.0j5.5.0....0...1c.1.64.serp..1.2.311.06cSKPTLo18\\\\\\\n        x26amp;safeui\\\\x3dimages\\\\x22 data-setprefs-on-url\\\\x3d\\\\x22/setprefs?authuser\\\\\\\n        x3d\\\\x26amp;sig\\\\x3d0_fI0GmKthH8jgM-1KWGWa9pya5ps%3D\\\\x26amp;prev\\\\x3dhttps://www.google.ru/search?newwindow%3D1%26safe%3Doff%26q%3Dxkbcomp%2Balt%2Bgr%26oq%3Dxkbcomp%2Balt%2Bgr%26gs_l%3Dserp.3..33i21.28976559.28977886.0.28978017.6.6.0.0.0.0.167.668.0j5.5.0....0...1c.1.64.serp..1.2.311.06cSKPTLo18\\\\\\\n        x26amp;safeui\\\\x3don\\\\x22 jsaction\\\\x3d\\\\x22dXIA6:hqPouf\\\\x22\\\\x3e\\\\x3cdiv\\\n        \\ class\\\\x3d\\\\x22jFQOsd\\\\x22\\\\x3e\\\\x3cspan\\\\x3eSafeSearch\\\\x3c/span\\\\x3e\\\\\\\n        x3cspan\\\\x3e\\\\x3ca class\\\\x3d\\\\x22W2x9Sc\\\\x22 href\\\\x3d\\\\x22/safesearch?prev\\\\\\\n        x3dhttps://www.google.ru/search?newwindow%3D1%26safe%3Doff%26q%3Dxkbcomp%2Balt%2Bgr%26oq%3Dxkbcomp%2Balt%2Bgr%26gs_l%3Dserp.3..33i21.28976559.28977886.0.28978017.6.6.0.0.0.0.167.668.0j5.5.0....0...1c.1.64.serp..1.2.311.06cSKPTLo18\\\\\\\n        x26amp;safe\\\\x3doff\\\\x26amp;sa\\\\x3dX\\\\x26amp;ved\\\\x3d2ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ8JsIegQICxAI\\\\\\\n        x22\\\\x3ePelajari lebih lanjut tentang SafeSearch\\\\x3c/a\\\\x3e\\\\x3c/span\\\\x3e\\\\\\\n        x3c/div\\\\x3e\\\\x3cdiv style\\\\x3d\\\\x22display:flex\\\\x22\\\\x3e\\\\x3cspan class\\\\\\\n        x3d\\\\x22Sqk7pf z1asCe v0Jmnb\\\\x22\\\\x3e\\\\x3csvg focusable\\\\x3d\\\\x22false\\\\\\\n        x22 xmlns\\\\x3d\\\\x22http://www.w3.org/2000/svg\\\\x22 viewBox\\\\x3d\\\\x220 0 24\\\n        \\ 24\\\\x22\\\\x3e\\\\x3cpath d\\\\x3d\\\\x22M12 17c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2\\\n        \\ 2 .9 2 2 2zm6-9h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0\\\n        \\ 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zM8.9 6c0-1.71 1.39-3.1 3.1-3.1s3.1\\\n        \\ 1.39 3.1 3.1v2H8.9V6zM18 20H6V10h12v10z\\\\x22\\\\x3e\\\\x3c/path\\\\x3e\\\\x3c/svg\\\\\\\n        x3e\\\\x3c/span\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\\\\n        x3d\\\\x22q0yked\\\\x22\\\\x3e\\\\x3ca href\\\\x3d\\\\x22/preferences?hl\\\\x3did\\\\x26amp;prev\\\\\\\n        x3dhttps://www.google.ru/search?newwindow%3D1%26safe%3Doff%26q%3Dxkbcomp%2Balt%2Bgr%26oq%3Dxkbcomp%2Balt%2Bgr%26gs_l%3Dserp.3..33i21.28976559.28977886.0.28978017.6.6.0.0.0.0.167.668.0j5.5.0....0...1c.1.64.serp..1.2.311.06cSKPTLo18#languages\\\\\\\n        x22 style\\\\x3d\\\\x22display:flex\\\\x22 data-jsarwt\\\\x3d\\\\x221\\\\x22 data-usg\\\\\\\n        x3d\\\\x22AOvVaw2kad61sp5q8y1_BoHplmU-\\\\x22 data-ved\\\\x3d\\\\x222ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ8ZsIegQICxAJ\\\\\\\n        x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22ZI7elf UCGAnb\\\\x22\\\\x3e\\\\x3cspan\\\\x3eBahasa\\\\\\\n        x3c/span\\\\x3e\\\\x3cspan style\\\\x3d\\\\x22font-size:12px\\\\x22\\\\x3e (Languages)\\\\\\\n        x3c/span\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cspan class\\\\x3d\\\\x22kQEH5b\\\\x22 style\\\\x3d\\\\\\\n        x22color:#70757a\\\\x22\\\\x3eIndonesia\\\\x3c/span\\\\x3e\\\\x3c/a\\\\x3e\\\\x3c/div\\\\\\\n        x3e\\\\x3cdiv class\\\\x3d\\\\x22q0yked\\\\x22\\\\x3e\\\\x3ca href\\\\x3d\\\\x22/advanced_search?q\\\\\\\n        x3dxkbcomp+alt+gr\\\\x26amp;newwindow\\\\x3d1\\\\x26amp;safe\\\\x3doff\\\\x26amp;hl\\\\\\\n        x3did\\\\x22 data-jsarwt\\\\x3d\\\\x221\\\\x22 data-usg\\\\x3d\\\\x22AOvVaw3e8ru2Iaj8_7hUIWhAYfyV\\\\\\\n        x22 data-ved\\\\x3d\\\\x222ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ7psIegQICxAK\\\\x22\\\\\\\n        x3e\\\\x3cspan class\\\\x3d\\\\x22ZI7elf\\\\x22\\\\x3ePenelusuran lanjutan\\\\x3c/span\\\\\\\n        x3e\\\\x3cspan style\\\\x3d\\\\x22color:#5f6368\\\\x22 class\\\\x3d\\\\x22z1asCe eznrjd\\\\\\\n        x22\\\\x3e\\\\x3csvg focusable\\\\x3d\\\\x22false\\\\x22 xmlns\\\\x3d\\\\x22http://www.w3.org/2000/svg\\\\\\\n        x22 viewBox\\\\x3d\\\\x220 0 24 24\\\\x22\\\\x3e\\\\x3cpath d\\\\x3d\\\\x22M19 19H5V5h7V3H5c-1.11\\\n        \\ 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83\\\n        \\ 9.83 1.41 1.41L19 6.41V10h2V3h-7z\\\\x22\\\\x3e\\\\x3c/path\\\\x3e\\\\x3c/svg\\\\x3e\\\\\\\n        x3c/span\\\\x3e\\\\x3c/a\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\x3d\\\\\\\n        x22m0uvVb\\\\x22 jsaction\\\\x3d\\\\x22ivUr0:rJpNrc\\\\x22\\\\x3e\\\\x3ch2 class\\\\x3d\\\\\\\n        x22kwWBYc\\\\x22\\\\x3eTampilan\\\\x3c/h2\\\\x3e\\\\x3cg-radio-button-group jsname\\\\\\\n        x3d\\\\x22sUvgTb\\\\x22 class\\\\x3d\\\\x22uWNHce H8eL7d j0iFNe\\\\x22 jscontroller\\\\\\\n        x3d\\\\x22SZXsif\\\\x22 aria-disabled\\\\x3d\\\\x22false\\\\x22 data-rwl\\\\x3d\\\\x221\\\\\\\n        x22 role\\\\x3d\\\\x22radiogroup\\\\x22 jsaction\\\\x3d\\\\x22keydown:uYT2Vb;focus:h06R8;blur:zjh6rb;rcuQ6b:npT2md\\\\\\\n        x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22GCYh9b\\\\x22 class\\\\x3d\\\\x22de2Dud\\\\x22 aria-checked\\\\\\\n        x3d\\\\x22false\\\\x22 aria-labelledby\\\\x3d\\\\x22_QyS5Y_SSL5HO5OUPp_mggAY_20\\\\\\\n        x22 data-index\\\\x3d\\\\x220\\\\x22 tabindex\\\\x3d\\\\x22-1\\\\x22 role\\\\x3d\\\\x22radio\\\\\\\n        x22 jsaction\\\\x3d\\\\x22g6cJHd\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22dsLpHe OvQkSb\\\\\\\n        x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22kaAgDc OvQkSb\\\\x22\\\\x3e\\\\x3c/div\\\\\\\n        x3e\\\\x3cdiv class\\\\x3d\\\\x22wT0tpe OvQkSb\\\\x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\\\\n        x3d\\\\x22dluk7e\\\\x22 id\\\\x3d\\\\x22_QyS5Y_SSL5HO5OUPp_mggAY_20\\\\x22\\\\x3e\\\\x3clabel\\\n        \\ class\\\\x3d\\\\x22GZcH3e LZTko\\\\x22 jsname\\\\x3d\\\\x22rWsIUb\\\\x22 style\\\\x3d\\\\\\\n        x22display:flex\\\\x22 data-ved\\\\x3d\\\\x222ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ9JsIegQICxAL\\\\\\\n        x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22UCGAnb\\\\x22\\\\x3eTema terang\\\\x3c/div\\\\x3e\\\\\\\n        x3cimg class\\\\x3d\\\\x22UCAEse\\\\x22 src\\\\x3d\\\\x22https://www.gstatic.com/ui/v1/menu/light_thumbnail2.png\\\\\\\n        x22 alt\\\\x3d\\\\x22\\\\x22\\\\x3e\\\\x3c/label\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\\\\n        x3cdiv jsname\\\\x3d\\\\x22GCYh9b\\\\x22 class\\\\x3d\\\\x22de2Dud\\\\x22 aria-checked\\\\\\\n        x3d\\\\x22false\\\\x22 aria-labelledby\\\\x3d\\\\x22_QyS5Y_SSL5HO5OUPp_mggAY_21\\\\\\\n        x22 data-index\\\\x3d\\\\x221\\\\x22 tabindex\\\\x3d\\\\x22-1\\\\x22 role\\\\x3d\\\\x22radio\\\\\\\n        x22 jsaction\\\\x3d\\\\x22g6cJHd\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22dsLpHe OvQkSb\\\\\\\n        x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22kaAgDc OvQkSb\\\\x22\\\\x3e\\\\x3c/div\\\\\\\n        x3e\\\\x3cdiv class\\\\x3d\\\\x22wT0tpe OvQkSb\\\\x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\\\\n        x3d\\\\x22dluk7e\\\\x22 id\\\\x3d\\\\x22_QyS5Y_SSL5HO5OUPp_mggAY_21\\\\x22\\\\x3e\\\\x3clabel\\\n        \\ class\\\\x3d\\\\x22GZcH3e LZTko\\\\x22 jsname\\\\x3d\\\\x22I7WXBf\\\\x22 style\\\\x3d\\\\\\\n        x22display:flex\\\\x22 data-ved\\\\x3d\\\\x222ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ9ZsIegQICxAM\\\\\\\n        x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22UCGAnb\\\\x22\\\\x3eTema gelap\\\\x3c/div\\\\x3e\\\\\\\n        x3cimg class\\\\x3d\\\\x22UCAEse\\\\x22 src\\\\x3d\\\\x22https://www.gstatic.com/ui/v1/menu/dark_thumbnail2.png\\\\\\\n        x22 alt\\\\x3d\\\\x22\\\\x22\\\\x3e\\\\x3c/label\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\\\\n        x3cdiv jsname\\\\x3d\\\\x22GCYh9b\\\\x22 class\\\\x3d\\\\x22de2Dud RvdoFd\\\\x22 aria-checked\\\\\\\n        x3d\\\\x22true\\\\x22 aria-labelledby\\\\x3d\\\\x22_QyS5Y_SSL5HO5OUPp_mggAY_22\\\\x22\\\n        \\ data-index\\\\x3d\\\\x222\\\\x22 tabindex\\\\x3d\\\\x220\\\\x22 role\\\\x3d\\\\x22radio\\\\\\\n        x22 jsaction\\\\x3d\\\\x22g6cJHd\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22dsLpHe OvQkSb\\\\\\\n        x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22kaAgDc OvQkSb\\\\x22\\\\x3e\\\\x3c/div\\\\\\\n        x3e\\\\x3cdiv class\\\\x3d\\\\x22wT0tpe OvQkSb\\\\x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\\\\n        x3d\\\\x22dluk7e\\\\x22 id\\\\x3d\\\\x22_QyS5Y_SSL5HO5OUPp_mggAY_22\\\\x22\\\\x3e\\\\x3clabel\\\n        \\ class\\\\x3d\\\\x22GZcH3e LZTko\\\\x22 jsname\\\\x3d\\\\x22qk0sxc\\\\x22 style\\\\x3d\\\\\\\n        x22display:flex\\\\x22 data-ved\\\\x3d\\\\x222ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ9psIegQICxAN\\\\\\\n        x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22UCGAnb\\\\x22\\\\x3eDefault perangkat\\\\x3c/div\\\\\\\n        x3e\\\\x3cimg class\\\\x3d\\\\x22UCAEse\\\\x22 src\\\\x3d\\\\x22https://www.gstatic.com/ui/v1/menu/device_default_thumbnail2.png\\\\\\\n        x22 alt\\\\x3d\\\\x22\\\\x22\\\\x3e\\\\x3c/label\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\\\\n        x3c/g-radio-button-group\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22m0uvVb\\\\\\\n        x22\\\\x3e\\\\x3ch2 class\\\\x3d\\\\x22kwWBYc\\\\x22\\\\x3eDukungan\\\\x3c/h2\\\\x3e\\\\x3cdiv\\\n        \\ class\\\\x3d\\\\x22q0yked\\\\x22\\\\x3e\\\\x3ca href\\\\x3d\\\\x22https://support.google.com/websearch/?p\\\\\\\n        x3ddsrp_search_hc\\\\x26amp;hl\\\\x3did\\\\x22 data-jsarwt\\\\x3d\\\\x221\\\\x22 data-usg\\\\\\\n        x3d\\\\x22AOvVaw1meHBHDv-4gwXKu8hMvYzs\\\\x22 data-ved\\\\x3d\\\\x222ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ8psIegQICxAO\\\\\\\n        x22\\\\x3e\\\\x3cspan class\\\\x3d\\\\x22ZI7elf\\\\x22\\\\x3eBantuan penelusuran\\\\x3c/span\\\\\\\n        x3e\\\\x3cspan style\\\\x3d\\\\x22color:#5f6368\\\\x22 class\\\\x3d\\\\x22z1asCe eznrjd\\\\\\\n        x22\\\\x3e\\\\x3csvg focusable\\\\x3d\\\\x22false\\\\x22 xmlns\\\\x3d\\\\x22http://www.w3.org/2000/svg\\\\\\\n        x22 viewBox\\\\x3d\\\\x220 0 24 24\\\\x22\\\\x3e\\\\x3cpath d\\\\x3d\\\\x22M19 19H5V5h7V3H5c-1.11\\\n        \\ 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83\\\n        \\ 9.83 1.41 1.41L19 6.41V10h2V3h-7z\\\\x22\\\\x3e\\\\x3c/path\\\\x3e\\\\x3c/svg\\\\x3e\\\\\\\n        x3c/span\\\\x3e\\\\x3c/a\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e');})();(function(){window.jsl.dh('_QyS5Y_SSL5HO5OUPp_mggAY_23','\\\n        \\  \\\\x3cdiv jsname\\\\x3d\\\\x22rSAM5d\\\\x22\\\\x3e \\\\x3cdiv style\\\\x3d\\\\x22opacity:0\\\\\\\n        x22 id\\\\x3d\\\\x22arc-stev\\\\x22 data-jiis\\\\x3d\\\\x22up\\\\x22 data-async-type\\\\\\\n        x3d\\\\x22arc\\\\x22 data-async-context-required\\\\x3d\\\\x22arc_id,q\\\\x22 class\\\\\\\n        x3d\\\\x22yp\\\\x22 data-async-rclass\\\\x3d\\\\x22search\\\\x22\\\\x3e\\\\x3c/div\\\\x3e\\\n        \\ \\\\x3c/div\\\\x3e \\\\x3cdiv jsname\\\\x3d\\\\x22UefMMd\\\\x22 class\\\\x3d\\\\x22Lm68h\\\\\\\n        x22 style\\\\x3d\\\\x22display:none\\\\x22\\\\x3e  \\\\x3cdiv class\\\\x3d\\\\x22rskU3c\\\\\\\n        x22 aria-valuetext\\\\x3d\\\\x22Memuat...\\\\x22 role\\\\x3d\\\\x22progressbar\\\\x22\\\\\\\n        x3e\\\\x3cdiv class\\\\x3d\\\\x22G8qI4b Ww4FFb vt6azd\\\\x22\\\\x3e\\\\x3cdiv class\\\\\\\n        x3d\\\\x22DYiTxe\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22N6dG3e\\\\x22\\\\x3e\\\\x3c/div\\\\\\\n        x3e\\\\x3cdiv class\\\\x3d\\\\x22e4XSEd\\\\x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\\\\n        x3cdiv class\\\\x3d\\\\x22AMbnUc\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22ysLSm\\\\x22\\\\\\\n        x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22G8qI4b\\\n        \\ Ww4FFb vt6azd\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22DYiTxe\\\\x22\\\\x3e\\\\x3cdiv\\\n        \\ class\\\\x3d\\\\x22N6dG3e\\\\x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22e4XSEd\\\\\\\n        x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22AMbnUc\\\\x22\\\\\\\n        x3e\\\\x3cdiv class\\\\x3d\\\\x22ysLSm\\\\x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\\\\n        x3e\\\\x3cdiv class\\\\x3d\\\\x22G8qI4b Ww4FFb vt6azd\\\\x22\\\\x3e\\\\x3cdiv class\\\\\\\n        x3d\\\\x22DYiTxe\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22N6dG3e\\\\x22\\\\x3e\\\\x3c/div\\\\\\\n        x3e\\\\x3cdiv class\\\\x3d\\\\x22e4XSEd\\\\x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\\\\n        x3cdiv class\\\\x3d\\\\x22AMbnUc\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22ysLSm\\\\x22\\\\\\\n        x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22G8qI4b\\\n        \\ Ww4FFb vt6azd\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22DYiTxe\\\\x22\\\\x3e\\\\x3cdiv\\\n        \\ class\\\\x3d\\\\x22N6dG3e\\\\x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22e4XSEd\\\\\\\n        x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22AMbnUc\\\\x22\\\\\\\n        x3e\\\\x3cdiv class\\\\x3d\\\\x22ysLSm\\\\x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\\\\n        x3e\\\\x3cdiv class\\\\x3d\\\\x22G8qI4b Ww4FFb vt6azd\\\\x22\\\\x3e\\\\x3cdiv class\\\\\\\n        x3d\\\\x22DYiTxe\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22N6dG3e\\\\x22\\\\x3e\\\\x3c/div\\\\\\\n        x3e\\\\x3cdiv class\\\\x3d\\\\x22e4XSEd\\\\x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\\\\n        x3cdiv class\\\\x3d\\\\x22AMbnUc\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22ysLSm\\\\x22\\\\\\\n        x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22G8qI4b\\\n        \\ Ww4FFb vt6azd\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22DYiTxe\\\\x22\\\\x3e\\\\x3cdiv\\\n        \\ class\\\\x3d\\\\x22N6dG3e\\\\x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22e4XSEd\\\\\\\n        x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22AMbnUc\\\\x22\\\\\\\n        x3e\\\\x3cdiv class\\\\x3d\\\\x22ysLSm\\\\x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\\\\n        x3e\\\\x3cdiv class\\\\x3d\\\\x22bOiwif\\\\x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e \\\\\\\n        x3c/div\\\\x3e \\\\x3cdiv jsname\\\\x3d\\\\x22Ei53Y\\\\x22 style\\\\x3d\\\\x22display:none\\\\\\\n        x22 data-ved\\\\x3d\\\\x222ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQsMoFegQIIhAA\\\\x22\\\\\\\n        x3e \\\\x3c/div\\\\x3e   ');})();(function(){window.jsl.dh('tsuid_11','\\\\x3cg-dialog\\\n        \\ jsname\\\\x3d\\\\x22BDbGbf\\\\x22 jscontroller\\\\x3d\\\\x22VEbNoe\\\\x22 data-id\\\\\\\n        x3d\\\\x22_QyS5Y_SSL5HO5OUPp_mggAY_13\\\\x22 jsaction\\\\x3d\\\\x22jxvro:Imgh9b\\\\\\\n        x22 jsdata\\\\x3d\\\\x22gctHtc;_;CQYHx8\\\\x22 jsshadow\\\\x3d\\\\x22\\\\x22\\\\x3e\\\\x3cdiv\\\n        \\ jsname\\\\x3d\\\\x22XKSfm\\\\x22 id\\\\x3d\\\\x22_QyS5Y_SSL5HO5OUPp_mggAY_13\\\\x22\\\n        \\ jsaction\\\\x3d\\\\x22dBhwS:TvD9Pc;mLt3mc\\\\x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/g-dialog\\\\\\\n        x3e');})();(function(){window.jsl.dh('_QyS5Y_SSL5HO5OUPp_mggAY_13','\\\\x3cdiv\\\n        \\ jsname\\\\x3d\\\\x22bF1uUb\\\\x22 class\\\\x3d\\\\x22t7xA6 Xz5tfb\\\\x22\\\\x3e\\\\x3c/div\\\\\\\n        x3e\\\\x3cdiv class\\\\x3d\\\\x22bErdLd llJxV hFCnyd wwYr3\\\\x22 jsaction\\\\x3d\\\\\\\n        x22trigger.dBhwS\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22ls8Qne\\\\x22 aria-hidden\\\\\\\n        x3d\\\\x22true\\\\x22 role\\\\x3d\\\\x22button\\\\x22 tabindex\\\\x3d\\\\x220\\\\x22 jsaction\\\\\\\n        x3d\\\\x22focus:sT2f3e\\\\x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3cspan jsslot\\\\x3d\\\\x22\\\\x22\\\n        \\ jsaction\\\\x3d\\\\x22mLt3mc\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22NJfJb TUOsUe mr5vfb\\\n        \\ Sr5CLc OosGzb\\\\x22 aria-labelledby\\\\x3d\\\\x22lQ3q8c\\\\x22 role\\\\x3d\\\\x22dialog\\\\\\\n        x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22C8RmQc\\\\x22 id\\\\x3d\\\\x22C8RmQc\\\\x22 data-jiis\\\\\\\n        x3d\\\\x22up\\\\x22 data-async-type\\\\x3d\\\\x22lbsc\\\\x22 class\\\\x3d\\\\x22yp\\\\x22\\\\\\\n        x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/span\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22ls8Qne\\\\\\\n        x22 aria-hidden\\\\x3d\\\\x22true\\\\x22 role\\\\x3d\\\\x22button\\\\x22 tabindex\\\\x3d\\\\\\\n        x220\\\\x22 jsaction\\\\x3d\\\\x22focus:tuePCd\\\\x22\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\\\\n        x3e');})();(function(){window.jsl.dh('tsuid_14','\\\\x3clocation-snackbar-with-learn-more\\\n        \\ jsname\\\\x3d\\\\x22nw18gf\\\\x22 jscontroller\\\\x3d\\\\x22khkNpe\\\\x22 jsaction\\\\\\\n        x3d\\\\x22sFrcje:No7Jhf\\\\x22\\\\x3e\\\\x3cspan id\\\\x3d\\\\x22tsuid_16\\\\x22\\\\x3e\\\\\\\n        x3c/span\\\\x3e\\\\x3c/location-snackbar-with-learn-more\\\\x3e');})();(function(){window.jsl.dh('tsuid_16','\\\\\\\n        x3cg-snackbar jsname\\\\x3d\\\\x22Ng57nc\\\\x22 jscontroller\\\\x3d\\\\x22OZLguc\\\\x22\\\n        \\ style\\\\x3d\\\\x22display:none\\\\x22 data-dismiss\\\\x3d\\\\x22\\\\x22 jsshadow\\\\\\\n        x3d\\\\x22\\\\x22 jsaction\\\\x3d\\\\x22rcuQ6b:npT2md\\\\x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\\\\n        x22sM5MNb\\\\x22 aria-live\\\\x3d\\\\x22polite\\\\x22 class\\\\x3d\\\\x22tYAdEe\\\\x22 style\\\\\\\n        x3d\\\\x22z-index:2000\\\\x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22Ng57nc\\\\x22 class\\\\\\\n        x3d\\\\x22FEXCIb\\\\x22 data-ved\\\\x3d\\\\x222ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ4G96BAgCEAU\\\\\\\n        x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22EA3l1b\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22Xb004\\\\\\\n        x22 jsslot\\\\x3d\\\\x22\\\\x22\\\\x3e\\\\x3cspan class\\\\x3d\\\\x22awHmMb wHYlTd yUTMj\\\\\\\n        x22\\\\x3eTidak dapat memperbarui lokasi Anda\\\\x3c/span\\\\x3e\\\\x3cg-snackbar-action\\\n        \\ class\\\\x3d\\\\x22BDp8nf zJUuqf AB4Wff Z7swgb\\\\x22 jsname\\\\x3d\\\\x22zrfavf\\\\\\\n        x22 jscontroller\\\\x3d\\\\x22xRxDld\\\\x22 jsaction\\\\x3d\\\\x22GtUzrb\\\\x22 data-ved\\\\\\\n        x3d\\\\x222ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQhbkIegQIAhAH\\\\x22\\\\x3e\\\\x3cg-flat-button\\\n        \\ class\\\\x3d\\\\x22U8shWc r2fjmd hObAcc gTewb VDgVie hpZDWd fSXIc\\\\x22 style\\\\\\\n        x3d\\\\x22color:#4285f4\\\\x22 role\\\\x3d\\\\x22button\\\\x22 tabindex\\\\x3d\\\\x220\\\\\\\n        x22\\\\x3e\\\\x3cspan class\\\\x3d\\\\x22Omzzbd\\\\x22\\\\x3ePelajari lebih lanjut\\\\x3c/span\\\\\\\n        x3e\\\\x3c/g-flat-button\\\\x3e\\\\x3c/g-snackbar-action\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\\\\n        x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/g-snackbar\\\\x3e');})();(function(){window.jsl.dh('tsuid_18','\\\\\\\n        x3cg-snackbar jsname\\\\x3d\\\\x22M8d6me\\\\x22 jscontroller\\\\x3d\\\\x22OZLguc\\\\x22\\\n        \\ style\\\\x3d\\\\x22display:none\\\\x22 jsshadow\\\\x3d\\\\x22\\\\x22 jsaction\\\\x3d\\\\\\\n        x22rcuQ6b:npT2md\\\\x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22sM5MNb\\\\x22 aria-live\\\\\\\n        x3d\\\\x22polite\\\\x22 class\\\\x3d\\\\x22tYAdEe\\\\x22 style\\\\x3d\\\\x22z-index:2000\\\\\\\n        x22\\\\x3e\\\\x3cdiv jsname\\\\x3d\\\\x22Ng57nc\\\\x22 class\\\\x3d\\\\x22FEXCIb\\\\x22 data-ved\\\\\\\n        x3d\\\\x222ahUKEwi0wOzM_LT8AhURJ7kGHac8CGAQ4G96BAgCEAk\\\\x22\\\\x3e\\\\x3cdiv class\\\\\\\n        x3d\\\\x22EA3l1b\\\\x22\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22Xb004\\\\x22 jsslot\\\\x3d\\\\x22\\\\\\\n        x22\\\\x3e\\\\x3cspan class\\\\x3d\\\\x22awHmMb wHYlTd yUTMj\\\\x22\\\\x3eMemperbarui\\\n        \\ lokasi...\\\\x3c/span\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\\\\n        x3e\\\\x3c/g-snackbar\\\\x3e');})();(function(){window.jsl.dh('_QyS5Y_SSL5HO5OUPp_mggAY_25','\\\\\\\n        x3cdiv\\\\x3e\\\\x3cdiv\\\\x3e\\\\x3cdiv class\\\\x3d\\\\x22gb_Nd\\\\x22\\\\x3eAplikasi Google\\\\\\\n        x3c/div\\\\x3e\\\\x3c/div\\\\x3e\\\\x3c/div\\\\x3e',function(){window.gbar&&gbar.up&&gbar.up.tp&&gbar.up.tp();this.gbar_=this.gbar_||{};(function(_){var\\\n        \\ window=this;\\ntry{\\n_.Vd=function(a,b,c){if(!a.o)if(c instanceof Array)for(var\\\n        \\ d of c)_.Vd(a,b,d);else{d=(0,_.x)(a.F,a,b);const e=a.B+c;a.B++;b.setAttribute(\\\"\\\n        data-eqid\\\",e);a.D[e]=d;b&&b.addEventListener?b.addEventListener(c,d,!1):b&&b.attachEvent?b.attachEvent(\\\"\\\n        on\\\"+c,d):a.A.log(Error(\\\"w`\\\"+b))}};\\n\\n}catch(e){_._DumpException(e)}\\n\\\n        try{\\n_.Wd=function(){if(!_.m.addEventListener||!Object.defineProperty)return!1;var\\\n        \\ a=!1,b=Object.defineProperty({},\\\"passive\\\",{get:function(){a=!0}});try{_.m.addEventListener(\\\"\\\n        test\\\",()=>{},b),_.m.removeEventListener(\\\"test\\\",()=>{},b)}catch(c){}return\\\n        \\ a}();\\n_.Xd=_.sb?\\\"webkitTransitionEnd\\\":\\\"transitionend\\\";\\n\\n}catch(e){_._DumpException(e)}\\n\\\n        try{\\nvar Yd=document.querySelector(\\\".gb_F .gb_d\\\"),Zd=document.querySelector(\\\"\\\n        #gb.gb_Oc\\\");Yd&&!Zd&&_.Vd(_.Md,Yd,\\\"click\\\");\\n\\n}catch(e){_._DumpException(e)}\\n\\\n        try{\\nvar Nh;_.Mh=function(a){if(a.B)return a.B;for(const b in a.j)if(a.j[b].yb()&&a.j[b].G())return\\\n        \\ a.j[b];return null};\\nNh=new class extends _.I{constructor(){var a=_.J;super();this.J=a;this.B=null;this.o={};this.M={};this.j={};this.F=null}D(a){a&&_.Mh(this)&&a!=_.Mh(this)&&_.Mh(this).ua(!1);this.B=a}G(a){a=this.j[a]||a;return\\\n        \\ _.Mh(this)==a}A(a,b){b=b.S();if(this.o[a]&&this.o[a][b])for(let c=0;c<this.o[a][b].length;c++)try{this.o[a][b][c]()}catch(d){this.J.log(d)}}L(a){return!this.M[a.S()]}H(a){this.j[a]&&(_.Mh(this)&&_.Mh(this).S()==a||this.j[a].ua(!0))}ab(a){this.F=a;for(const\\\n        \\ b in this.j)this.j[b].yb()&&this.j[b].ab(a)}C(a){this.j[a.S()]=\\na}Cc(a){return\\\n        \\ a in this.j?this.j[a]:null}};_.md(\\\"dd\\\",Nh);\\n\\n}catch(e){_._DumpException(e)}\\n\\\n        try{\\nvar mj=document.querySelector(\\\".gb_b .gb_d\\\"),nj=document.querySelector(\\\"\\\n        #gb.gb_Oc\\\");mj&&!nj&&_.Vd(_.Md,mj,\\\"click\\\");\\n\\n}catch(e){_._DumpException(e)}\\n\\\n        })(this.gbar_);\\n// Google Inc.\\n;this.gbar_=this.gbar_||{};(function(_){var\\\n        \\ window=this;\\ntry{\\n_.$d=function(a,b){return 0<=_.lb(a,b)};_.ae=function(a){var\\\n        \\ b=typeof a;return\\\"object\\\"!=b?b:a?Array.isArray(a)?\\\"array\\\":b:\\\"null\\\"\\\n        };_.be=function(a){var b=_.ae(a);return\\\"array\\\"==b||\\\"object\\\"==b&&\\\"number\\\"\\\n        ==typeof a.length};_.ce=function(a,b){var c=Array.prototype.slice.call(arguments,1);return\\\n        \\ function(){var d=c.slice();d.push.apply(d,arguments);return a.apply(this,d)}};try{(new\\\n        \\ self.OffscreenCanvas(0,0)).getContext(\\\"2d\\\")}catch(a){}_.de=_.B||_.sb;\\n\\\n        _.ee=function(a,b){this.width=a;this.height=b};_.l=_.ee.prototype;_.l.aspectRatio=function(){return\\\n        \\ this.width/this.height};_.l.mc=function(){return!(this.width*this.height)};_.l.ceil=function(){this.width=Math.ceil(this.width);this.height=Math.ceil(this.height);return\\\n        \\ this};_.l.floor=function(){this.width=Math.floor(this.width);this.height=Math.floor(this.height);return\\\n        \\ this};_.l.round=function(){this.width=Math.round(this.width);this.height=Math.round(this.height);return\\\n        \\ this};\\nvar ge,je;_.fe=function(a,b){return(b||document).getElementsByTagName(String(a))};_.he=function(a,b){_.ab(b,function(c,d){c&&\\\"\\\n        object\\\"==typeof c&&c.Yb&&(c=c.Ib());\\\"style\\\"==d?a.style.cssText=c:\\\"class\\\"\\\n        ==d?a.className=c:\\\"for\\\"==d?a.htmlFor=c:ge.hasOwnProperty(d)?a.setAttribute(ge[d],c):0==d.lastIndexOf(\\\"\\\n        aria-\\\",0)||0==d.lastIndexOf(\\\"data-\\\",0)?a.setAttribute(d,c):a[d]=c})};\\n\\\n        ge={cellpadding:\\\"cellPadding\\\",cellspacing:\\\"cellSpacing\\\",colspan:\\\"colSpan\\\"\\\n        ,frameborder:\\\"frameBorder\\\",height:\\\"height\\\",maxlength:\\\"maxLength\\\",nonce:\\\"\\\n        nonce\\\",role:\\\"role\\\",rowspan:\\\"rowSpan\\\",type:\\\"type\\\",usemap:\\\"useMap\\\"\\\n        ,valign:\\\"vAlign\\\",width:\\\"width\\\"};_.ke=function(a,b){var c=b[1],d=_.ie(a,String(b[0]));c&&(\\\"\\\n        string\\\"===typeof c?d.className=c:Array.isArray(c)?d.className=c.join(\\\" \\\"\\\n        ):_.he(d,c));2<b.length&&je(a,d,b);return d};\\nje=function(a,b,c){function\\\n        \\ d(h){h&&b.appendChild(\\\"string\\\"===typeof h?a.createTextNode(h):h)}for(var\\\n        \\ e=2;e<c.length;e++){var f=c[e];if(!_.be(f)||_.eb(f)&&0<f.nodeType)d(f);else{a:{if(f&&\\\"\\\n        number\\\"==typeof f.length){if(_.eb(f)){var g=\\\"function\\\"==typeof f.item||\\\"\\\n        string\\\"==typeof f.item;break a}if(\\\"function\\\"===typeof f){g=\\\"function\\\"\\\n        ==typeof f.item;break a}}g=!1}_.mb(g?_.ma(f):f,d)}}};_.le=function(a){return\\\n        \\ _.ie(document,a)};\\n_.ie=function(a,b){b=String(b);\\\"application/xhtml+xml\\\"\\\n        ===a.contentType&&(b=b.toLowerCase());return a.createElement(b)};_.me=function(a){for(var\\\n        \\ b;b=a.firstChild;)a.removeChild(b)};_.ne=function(a){return _.eb(a)&&1==a.nodeType};_.oe=function(a){return\\\n        \\ 9==a.nodeType?a:a.ownerDocument||a.document};_.pe=function(a,b){for(var\\\n        \\ c=0;a;){if(b(a))return a;a=a.parentNode;c++}return null};\\n\\n}catch(e){_._DumpException(e)}\\n\\\n        try{\\nvar Fe;_.Ce=function(a){return/^[\\\\s\\\\xa0]*$/.test(a)};_.De=function(a,b){if(void\\\n        \\ 0!==a.Ea||void 0!==a.j)throw Error(\\\"B\\\");a.j=b;_.Dd(a)};_.Ee=class extends\\\n        \\ _.H{constructor(a){super(a)}};Fe=0;_.Ge=function(a){return Object.prototype.hasOwnProperty.call(a,_.fb)&&a[_.fb]||(a[_.fb]=++Fe)};_.He=function(a){return\\\n        \\ _.od(_.kd.j(),a)};\\n\\n}catch(e){_._DumpException(e)}\\ntry{\\n_.rj=function(a,b,c){a.rel=c;-1!=c.toLowerCase().indexOf(\\\"\\\n        stylesheet\\\")?(a.href=_.Cc(b),(b=_.ad(a.ownerDocument&&a.ownerDocument.defaultView))&&a.setAttribute(\\\"\\\n        nonce\\\",b)):a.href=b instanceof _.Ac?_.Cc(b):b instanceof _.Ec?_.Fc(b):_.Fc(_.Kc(b))};\\n\\\n        \\n}catch(e){_._DumpException(e)}\\ntry{\\n_.sj=function(a){const b=_.vc();a=b?b.createScriptURL(a):a;return\\\n        \\ new _.Ac(a,_.zc)};\\n/*\\n\\n SPDX-License-Identifier: Apache-2.0\\n*/\\nvar\\\n        \\ tj;try{new URL(\\\"s://g\\\"),tj=!0}catch(a){tj=!1}_.uj=tj;\\n\\n}catch(e){_._DumpException(e)}\\n\\\n        try{\\n_.vj=function(a){var b;let c;const d=null==(c=(b=(a.ownerDocument&&a.ownerDocument.defaultView||window).document).querySelector)?void\\\n        \\ 0:c.call(b,\\\"script[nonce]\\\");(b=d?d.nonce||d.getAttribute(\\\"nonce\\\")||\\\"\\\n        \\\":\\\"\\\")&&a.setAttribute(\\\"nonce\\\",b)};\\n\\n}catch(e){_._DumpException(e)}\\n\\\n        try{\\nvar wj=function(a,b,c){_.Ld.log(46,{att:a,max:b,url:c})},yj=function(a,b,c){_.Ld.log(47,{att:a,max:b,url:c});a<b?xj(a+1,b):_.J.log(Error(\\\"\\\n        $`\\\"+a+\\\"`\\\"+b),{url:c})},xj=function(a,b){if(zj){const d=_.le(\\\"SCRIPT\\\"\\\n        );d.async=!0;d.type=\\\"text/javascript\\\";d.charset=\\\"UTF-8\\\";var c=d;c.src=_.Bc(zj);_.vj(c);d.onload=_.ce(wj,a,b,d.src);d.onerror=_.ce(yj,a,b,d.src);_.Ld.log(45,{att:a,max:b,url:d.src});_.fe(\\\"\\\n        HEAD\\\")[0].appendChild(d)}},Aj=class extends _.H{constructor(a){super(a)}},Bj=_.F(_.Gd,Aj,17)||new\\\n        \\ Aj,Cj,zj=(Cj=\\n_.F(Bj,_.jc,1))?_.sj(_.C(Cj,4)||\\\"\\\"):null,Dj,Ej=(Dj=_.F(Bj,_.jc,2))?_.sj(_.C(Dj,4)||\\\"\\\n        \\\"):null,Fj=function(){xj(1,2);if(Ej){const a=_.le(\\\"LINK\\\");a.setAttribute(\\\"\\\n        type\\\",\\\"text/css\\\");_.rj(a,Ej,\\\"stylesheet\\\");let b=_.ad();b&&a.setAttribute(\\\"\\\n        nonce\\\",b);_.fe(\\\"HEAD\\\")[0].appendChild(a)}};\\n(function(){const a=_.Hd();if(_.E(a,18))Fj();else{const\\\n        \\ b=_.C(a,19)||0;window.addEventListener(\\\"load\\\",()=>{window.setTimeout(Fj,b)})}})();\\n\\\n        \\n}catch(e){_._DumpException(e)}\\n})(this.gbar_);\\n// Google Inc.\\n;});})();(function(){google.drty&&google.drty(undefined,true);})();});</script><div></div><div\\\n        \\ jscontroller=\\\"MTV2Lb\\\" style=\\\"display:none\\\" src=\\\"/uviewer?q=xkbcomp+alt+gr&amp;newwindow=1&amp;safe=off&amp;origin=https%3A%2F%2Fwww.google.ru\\\"\\\n        \\ id=\\\"Rvx4kc\\\" jsaction=\\\"rcuQ6b:npT2md;u0pjoe:Hq0NGf\\\"></div><div jscontroller=\\\"\\\n        W0N1pf\\\" id=\\\"DDeXhf\\\" jsaction=\\\"u0pjoe:Hq0NGf\\\"></div><div id=\\\"lfootercc\\\"\\\n        ><script nonce=\\\"fiRsZQKlRm7AeAysGAgLGA\\\">(function(){for(var i in google.iir||{}){_setImagesSrc([i],google.iir[i]);}google.iir={};})();google.jslm=3;</script><div\\\n        \\ id=\\\"reviewDialog\\\" data-async-context=\\\"async_id_prefix:\\\" data-jiis=\\\"\\\n        up\\\" data-async-type=\\\"reviewDialog\\\" data-async-context-required=\\\"async_id_prefix\\\"\\\n        \\ class=\\\"yp\\\"></div><div id=\\\"dbg_\\\"></div></div></body></html>\"\n    headers:\n      Alt-Svc:\n      - h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\";\n        ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\"\n      Cache-Control:\n      - private, max-age=0\n      Content-Security-Policy:\n      - 'object-src ''none'';base-uri ''self'';script-src ''nonce-fiRsZQKlRm7AeAysGAgLGA''\n        ''strict-dynamic'' ''report-sample'' ''unsafe-eval'' ''unsafe-inline'' https:\n        http:;report-uri https://csp.withgoogle.com/csp/gws/fff'\n      Content-Type:\n      - text/html; charset=UTF-8\n      Cross-Origin-Opener-Policy-Report-Only:\n      - same-origin-allow-popups; report-to=\"gws\"\n      Date:\n      - Sat, 07 Jan 2023 07:50:27 GMT\n      Expires:\n      - '-1'\n      P3P:\n      - CP=\"This is not a P3P policy! See g.co/p3phelp for more info.\"\n      Report-To:\n      - '{\"group\":\"gws\",\"max_age\":2592000,\"endpoints\":[{\"url\":\"https://csp.withgoogle.com/csp/report-to/gws/fff\"}]}'\n      Server:\n      - gws\n      Set-Cookie:\n      - 1P_JAR=2023-01-07-07; expires=Mon, 06-Feb-2023 07:50:27 GMT; path=/; domain=.google.ru;\n        Secure; SameSite=none\n      - AEC=ARSKqsKQNboYinipllm8z9W-j7W4wEQcQ8U1KijhcwNL4qQSJCmWe5aLdA; expires=Thu,\n        06-Jul-2023 07:50:27 GMT; path=/; domain=.google.ru; Secure; HttpOnly; SameSite=lax\n      - NID=511=TrY4zPK8E2AgNqCwDKQ0JIX0J6gBLfbfbtQqbiMj5cOpKzRzHqIXLszjmk4ieklqVmh02oNjjJbGSX_tj_XnZeiYvVrOQ5zYPoX2in5AgFRz-WpdGhch0FzkgBpVyq7cNznHvbx-uHPuD4t8UOQQreMMiCsZiLM_WeNpkQWhljs;\n        expires=Sun, 09-Jul-2023 07:50:27 GMT; path=/; domain=.google.ru; Secure;\n        HttpOnly; SameSite=none\n      Strict-Transport-Security:\n      - max-age=31536000\n      Transfer-Encoding:\n      - chunked\n      X-Frame-Options:\n      - SAMEORIGIN\n      X-XSS-Protection:\n      - '0'\n    status:\n      code: 200\n      message: OK\nversion: 1\n"
  },
  {
    "path": "tests/genbm.sh",
    "content": "#!/bin/bash\n\n# Scriptlet to auto-generate buku bookmarks\n# Usage: genbm.sh n\n#        where, n = number of records to generate\n#\n# Author: Arun Prakash Jana (engineerarun@gmail.com)\n\nif [ \"$#\" -ne 1 ]; then\n    echo \"usage: genbm n\"\n    exit 1\nfi\n\ncount=0\n\nwhile [ $count -lt \"$1\" ]; do\n    url=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)\n    buku -a https://www.$url.com --title Dummy bookmark for testing. --tag auto-generated, dummy bookmark --comment Generated from the test script $count.\n    let count=count+1\ndone\n"
  },
  {
    "path": "tests/pytest.ini",
    "content": "[pytest]\ntimeout = 10\ntimeout_method = thread\nmarkers =\n  non_tox: not run on tox\n  slow: slow tests\n  gui: GUI (functional) tests\n"
  },
  {
    "path": "tests/test_BukuCrypt.py",
    "content": "\"\"\"test module.\"\"\"\nimport os\nimport random\n\nimport pytest\n\n\ndef test_get_filehash(tmpdir):\n    \"\"\"test method.\"\"\"\n    exp_res = b'\\x9f\\x86\\xd0\\x81\\x88L}e\\x9a/\\xea\\xa0\\xc5Z\\xd0\\x15\\xa3\\xbfO\\x1b+\\x0b\\x82,\\xd1]l\\x15\\xb0\\xf0\\n\\x08'  # NOQA\n    test_file = os.path.join(tmpdir.strpath, 'my_test_file.txt')\n    with open(test_file, 'w', encoding=\"utf8\", errors=\"surrogateescape\") as f:\n        f.write('test')\n    from buku import BukuCrypt\n    res = BukuCrypt.get_filehash(test_file)\n    assert res == exp_res\n\n\n@pytest.mark.slow\n@pytest.mark.parametrize(\n    'filesize',\n    list(range(0, 17)) + [511, 512, 513, 1023, 1024, 1025, 524288, 524289, 1000000, 1048576, 2097152, 4194304]\n)\ndef test_encrypt_decrypt(tmpdir, filesize):\n    \"\"\"test method.\"\"\"\n    dbfile = os.path.join(tmpdir.strpath, 'test_encrypt_decrypt_dbfile')\n    content = bytes(random.getrandbits(8) for _ in range(filesize))\n    with open(dbfile, 'wb') as fp:\n        fp.write(content)\n    assert os.stat(dbfile).st_size == filesize\n    from buku import BukuCrypt\n    BukuCrypt.encrypt_file(1, dbfile=dbfile, password='password')\n    BukuCrypt.decrypt_file(1, dbfile=dbfile, password='password')\n    assert os.path.exists(dbfile)\n    with open(dbfile, 'rb') as fp:\n        roundtrip_content = fp.read()\n    assert roundtrip_content == content\n"
  },
  {
    "path": "tests/test_ExtendedArgumentParser.py",
    "content": "\"\"\"test module.\"\"\"\nfrom itertools import product\nfrom unittest import mock\n\nimport pytest\n\n\n@pytest.mark.parametrize(\"platform, file\", product(['win32', 'linux'], [None, mock.Mock()]))\ndef test_program_info(platform, file):\n    \"\"\"test method.\"\"\"\n    with mock.patch('buku.sys') as m_sys:\n        import buku\n        file = mock.Mock()\n        if file is None:\n            buku.ExtendedArgumentParser.program_info()\n        else:\n            buku.ExtendedArgumentParser.program_info(file)\n        if platform == 'win32' and file == m_sys.stdout:\n            assert len(m_sys.stderr.write.mock_calls) == 1\n        else:\n            assert len(file.write.mock_calls) == 1\n\n\ndef test_prompt_help():\n    \"\"\"test method.\"\"\"\n    file = mock.Mock()\n    import buku\n    buku.ExtendedArgumentParser.prompt_help(file)\n    assert len(file.write.mock_calls) == 1\n\n\ndef test_print_help():\n    \"\"\"test method.\"\"\"\n    file = mock.Mock()\n    import buku\n    obj = buku.ExtendedArgumentParser()\n    obj.program_info = mock.Mock()\n    obj.print_help(file)\n    obj.program_info.assert_called_once_with(file)\n"
  },
  {
    "path": "tests/test_buku.py",
    "content": "\"\"\"test module.\"\"\"\nimport json\nimport logging\nimport os\nimport signal\nimport unittest\nfrom itertools import product\nfrom textwrap import dedent\nfrom configparser import ConfigParser\nfrom unittest import mock\nfrom urllib.parse import urlparse\n\nimport pytest\n\nfrom buku import DELIM, FIELD_FILTER, ALL_FIELDS, SortKey, FetchResult, is_int, prep_tag_search, \\\n                 print_rec_with_filter, get_netloc, extract_auth, parse_range, split_by_marker\n\n\ndef check_import_html_results_contains(result, expected_result):\n    count = 0\n    for r in result:\n        for idx, exp_r in enumerate(expected_result):\n            if r == exp_r:\n                count += idx\n    n = len(expected_result) - 1\n    return count == n * (n + 1) / 2\n\n\n@pytest.mark.parametrize('url, result', [\n    ('http://user:password@hostname:1234/path?query#hash', 'user:password'),\n    ('http://:password@hostname:1234/path?query#hash', ':password'),\n    ('http://user:@hostname:1234/path?query#hash', 'user:'),\n    ('http://user@hostname:1234/path?query#hash', 'user'),\n    ('http://@hostname:1234/path?query#hash', ''),\n    ('http://hostname:1234/path?query#hash', None),\n    ('//[', ValueError('Invalid IPv6 URL')),\n    ('//⁈', ValueError(\"netloc '⁈' contains invalid characters under NFKC normalization\")),\n])\ndef test_extract_auth(url, result):\n    if not isinstance(result, Exception):\n        assert extract_auth(url) == (result, 'http://hostname:1234/path?query#hash')\n    else:\n        try:\n            extract_auth(url)\n        except Exception as e:\n            assert repr(e) == repr(result)\n        else:\n            assert False, f'expected {repr(result)} to be raised'\n\n\n@pytest.mark.parametrize('url, netloc', [\n    ['http://example.com', 'example.com'],\n    ['example.com/#foo/bar', 'example.com'],\n    ['ftp://ftp.somedomain.org', 'ftp.somedomain.org'],\n    ['about:newtab', None],\n    ['chrome://version/', 'version'],\n    ['javascript:void(0.0)', None],\n    ['data:,text.with.dots', None],\n    ['http://[', None],  # parsing error\n    ['http://⁈', None],  # parsing error\n])\ndef test_get_netloc(url, netloc):\n    assert get_netloc(url) == netloc\n\n\n@pytest.mark.parametrize('url, exp_res', [\n    ['http://example.com', False],\n    ['example.com/#foo/bar', False],\n    ['ftp://ftp.somedomain.org', False],\n    ['http://examplecom.', True],   # ends with a '.'\n    ['http://.example.com', True],  # starts with a '.'\n    ['http://example.com.', True],  # ends with a '.'\n    ['about:newtab', True],\n    ['chrome://version/', True],    # contains no '.'\n    ['javascript:void(0.0)', True],\n    ['data:,text.with.dots', True],\n    ['http://[', True],             # parsing error\n    ['http://⁈', True],             # parsing error\n])\ndef test_is_bad_url(url, exp_res):\n    import buku\n\n    res = buku.is_bad_url(url)\n    assert res == exp_res\n\n\n@pytest.mark.parametrize(\n    \"url, exp_res\",\n    [\n        (\"http://example.com/file.pdf\", True),\n        (\"http://example.com/file.txt\", True),\n        (\"http://example.com/file.jpg\", False),\n    ],\n)\ndef test_is_ignored_mime(url, exp_res):\n    \"\"\"test func.\"\"\"\n    import buku\n\n    assert exp_res == buku.is_ignored_mime(url)\n\n\ndef test_gen_headers():\n    \"\"\"test func.\"\"\"\n    import buku\n\n    exp_myheaders = {\n        'Accept-Encoding': 'gzip,deflate',\n        'User-Agent': buku.USER_AGENT,\n        'Sec-Fetch-Mode': 'navigate',\n        'Accept': '*/*',\n        'Cookie': '',\n        'DNT': '1',\n    }\n    buku.gen_headers()\n    assert buku.MYPROXY is None\n    assert buku.MYHEADERS == exp_myheaders\n\n\n@pytest.mark.parametrize(\"m_myproxy\", [None, mock.Mock()])\ndef test_get_PoolManager(m_myproxy):\n    \"\"\"test func.\"\"\"\n    with mock.patch(\"buku.urllib3\"):\n        import buku\n\n        buku.myproxy = m_myproxy\n        assert buku.get_PoolManager()\n\n\n@pytest.mark.parametrize(\n    \"keywords, exp_res\",\n    [\n        ([\"\"], DELIM),\n        ([\",\"], DELIM),\n        ([\"tag1, tag2\"], \",tag1,tag2,\"),\n        ([\" a tag , ,   ,  ,\\t,\\n,\\r,\\x0b,\\x0c\"], \",a tag,\"),  # whitespaces\n        ([\",,,,,\"], \",\"),  # empty tags\n        ([\"\\\"tag\\\",'tag',tag\"], \",\\\"tag\\\",'tag',tag,\"),  # escaping quotes\n        ([\"tag,tag, tag,  tag,tag , tag \"], \",tag,\"),  # duplicates, excessive spaces\n        ([\"tag1\", \"tag2\", \"tag3\"], \",tag1 tag2 tag3,\"),\n        ([\"tag1\", \"tag2\"], \",tag1 tag2,\"),\n        ([\"tag1\"], \",tag1,\"),\n        ([\"tag1,tag2\", \"tag3\"], \",tag1,tag2 tag3,\"),\n        ([\"tag1,tag2\", \"tag3,tag4\"], \",tag1,tag2 tag3,tag4,\"),\n        ([\"tag1,tag2\"], \",tag1,tag2,\"),\n        ([\"z_tag,a_tag,n_tag\"], \",a_tag,n_tag,z_tag,\"),  # sorting tags\n        ([\"  \"], \",\"),\n        ([\"\"], \",\"),\n        ([\",\"], \",\"),\n        ([], \",\"),  # call with empty list\n        ([None], \",\"),\n        (None, None),  # call with None\n        # combo\n        (\n            [',,z_tag, a tag ,\\t,,,  ,n_tag ,n_tag, a_tag, \\na tag  ,\\r, \"a_tag\"'],\n            ',\"a_tag\",a tag,a_tag,n_tag,z_tag,',\n        ),\n    ],\n)\n@pytest.mark.parametrize('prefix', [None, '', '+', '-'])\ndef test_parse_tags(prefix, keywords, exp_res):\n    \"\"\"test func.\"\"\"\n    import buku\n\n    edit_input = prefix is not None\n    if keywords is None:\n        assert buku.parse_tags(keywords, edit_input=edit_input) is None\n    else:\n        _keywords = ([] if not prefix else [prefix]) + keywords\n        assert buku.parse_tags(_keywords, edit_input=edit_input) == (prefix or '') + exp_res\n\n\ndef test_parse_tags_no_args():\n    import buku\n\n    assert buku.parse_tags() == DELIM\n\n\n@pytest.mark.parametrize(\"field_filter, exp_res\", [\n    (0, [\"1. title1\\n   > http://url1.com\\n   + desc1\\n   # tag1\\n\",\n         \"2. title2\\n   > http://url2.com\\n   + desc2\\n   # tag1,tag2\\n\"]),\n    (1, [\"1\\thttp://url1.com\", \"2\\thttp://url2.com\"]),\n    (2, [\"1\\thttp://url1.com\\ttag1\", \"2\\thttp://url2.com\\ttag1,tag2\"]),\n    (3, [\"1\\ttitle1\", \"2\\ttitle2\"]),\n    (4, [\"1\\thttp://url1.com\\ttitle1\\ttag1\", \"2\\thttp://url2.com\\ttitle2\\ttag1,tag2\"]),\n    (5, [\"1\\ttitle1\\ttag1\", \"2\\ttitle2\\ttag1,tag2\"]),\n    (10, [\"http://url1.com\", \"http://url2.com\"]),\n    (20, [\"http://url1.com\\ttag1\", \"http://url2.com\\ttag1,tag2\"]),\n    (30, [\"title1\", \"title2\"]),\n    (40, [\"http://url1.com\\ttitle1\\ttag1\", \"http://url2.com\\ttitle2\\ttag1,tag2\"]),\n    (50, [\"title1\\ttag1\", \"title2\\ttag1,tag2\"]),\n])\ndef test_print_rec_with_filter(capfd, field_filter, exp_res):\n    records = [(1, \"http://url1.com\", \"title1\", \",tag1,\", \"desc1\"),\n               (2, \"http://url2.com\", \"title2\", \",tag1,tag2,\", \"desc2\")]\n    print_rec_with_filter(records, field_filter)\n    assert capfd.readouterr().out == ''.join(f'{s}\\n' for s in exp_res)\n\n\n@pytest.mark.parametrize(\n    \"taglist, exp_res\",\n    [\n        [\"tag1, tag2+3\", ([\",tag1,\", \",tag2+3,\"], \"OR\", None)],\n        [\"tag1 + tag2-3 + tag4\", ([\",tag1,\", \",tag2-3,\", \",tag4,\"], \"AND\", None)],\n        [\"tag1, tag2-3 - tag4, tag5\", ([\",tag1,\", \",tag2-3,\"], \"OR\", \",tag4,|,tag5,\")],\n    ],\n)\ndef test_prep_tag_search(taglist, exp_res):\n    \"\"\"test prep_tag_search helper function\"\"\"\n\n    results = prep_tag_search(taglist)\n    assert results == exp_res\n\n\n@pytest.mark.parametrize(\n    \"nav, is_editor_valid_retval, edit_rec_retval\",\n    product(\n        [\"w\", [None, None, 1], [None, None, \"string\"]],\n        [True, False],\n        [[mock.Mock(), mock.Mock(), mock.Mock(), mock.Mock()], None],\n    ),\n)\ndef test_edit_at_prompt(nav, is_editor_valid_retval, edit_rec_retval):\n    \"\"\"test func.\"\"\"\n    obj = mock.Mock()\n    editor = mock.Mock()\n    with mock.patch(\"buku.get_system_editor\", return_value=editor), mock.patch(\n        \"buku.is_editor_valid\", return_value=is_editor_valid_retval\n    ), mock.patch(\"buku.edit_rec\", return_value=edit_rec_retval) as m_edit_rec:\n        import buku\n\n        buku.edit_at_prompt(obj, nav)\n        # test\n        if nav == \"w\" and not is_editor_valid_retval:\n            return\n        if nav == \"w\":\n            m_edit_rec.assert_called_once_with(editor, \"\", None, buku.DELIM, None)\n        elif buku.is_int(nav[2:]):\n            obj.edit_update_rec.assert_called_once_with(int(nav[2:]))\n            return\n        else:\n            editor = nav[2:]\n        m_edit_rec.assert_called_once_with(editor, \"\", None, buku.DELIM, None)\n        if edit_rec_retval is not None:\n            obj.add_rec(*edit_rec_retval)\n\n\n@pytest.mark.parametrize('single_record', [True, False])\n@pytest.mark.parametrize('field_filter', [0, 1, 2, 3, 4, 5, 10, 20, 30, 40, 50])\ndef test_format_json(field_filter, single_record):\n    resultset = [[f'<row{x}>' for x in range(5)]]\n    fields = FIELD_FILTER.get(field_filter, ALL_FIELDS)\n    marks = {}\n    if 'id' in fields:\n        marks['index'] = '<row0>'\n    if 'url' in fields:\n        marks['uri'] = '<row1>'\n    if 'title' in fields:\n        marks['title'] = '<row2>'\n    if 'tags' in fields:\n        marks['tags'] = 'row3'\n    if 'desc' in fields:\n        marks['description'] = '<row4>'\n    if not single_record:\n        marks = [marks]\n\n    with mock.patch(\"buku.json\") as m_json:\n        import buku\n\n        res = buku.format_json(resultset, single_record, field_filter)\n        m_json.dumps.assert_called_once_with(marks, sort_keys=True, indent=4)\n        assert res == m_json.dumps.return_value\n\n\n@pytest.mark.parametrize(\n    \"string, exp_res\",\n    [\n        (\"string\", False),\n        (\"12\", True),\n        (\"12.1\", False),\n    ],\n)\ndef test_is_int(string, exp_res):\n    \"\"\"test func.\"\"\"\n    import buku\n\n    assert exp_res == buku.is_int(string)\n\n\n@pytest.mark.parametrize(\n    \"url, opened_url, platform\",\n    [\n        [\"http://example.com\", \"http://example.com\", \"linux\"],\n        [\"example.com\", \"http://example.com\", \"linux\"],\n        [\"http://example.com\", \"http://example.com\", \"win32\"],\n    ],\n)\ndef test_browse(url, opened_url, platform):\n    \"\"\"test func.\"\"\"\n    with mock.patch(\"buku.webbrowser\") as m_webbrowser, mock.patch(\"buku.sys\") as m_sys, mock.patch(\"buku.os\"):\n        m_sys.platform = platform\n        get_func_retval = mock.Mock()\n        m_webbrowser.get.return_value = get_func_retval\n        import buku\n\n        buku.browse.suppress_browser_output = True\n        buku.browse.override_text_browser = False\n        buku.browse(url)\n        if platform == \"win32\":\n            m_webbrowser.open.assert_called_once_with(opened_url, new=2)\n        else:\n            get_func_retval.open.assert_called_once_with(opened_url, new=2)\n\n\n@pytest.mark.parametrize(\"status_code, latest_release\", product([200, 404], [True, False]))\ndef test_check_upstream_release(status_code, latest_release):\n    \"\"\"test func.\"\"\"\n    resp = mock.Mock()\n    resp.status = status_code\n    m_manager = mock.Mock()\n    m_manager.request.return_value = resp\n    with mock.patch(\"buku.urllib3\") as m_urllib3, mock.patch(\"buku.print\") as m_print:\n        import buku\n\n        if latest_release:\n            latest_version = \"v{}\".format(buku.__version__)\n        else:\n            latest_version = \"v0\"\n        m_urllib3.PoolManager.return_value = m_manager\n        resp.data.decode.return_value = json.dumps([{\"tag_name\": latest_version}])\n        buku.check_upstream_release()\n        if status_code != 200:\n            return\n        len(m_print.mock_calls) == 1\n\n\n@pytest.mark.parametrize(\n    \"exp, item, exp_res\",\n    [\n        (\"cat.y\", \"catty\", True),\n        (\"cat.y\", \"caty\", False),\n    ],\n)\ndef test_regexp(exp, item, exp_res):\n    \"\"\"test func.\"\"\"\n    import buku\n\n    res = buku.regexp(exp, item)\n    assert res == exp_res\n\n\n@pytest.mark.parametrize(\"token, exp_res\", [(\"text\", \",text,\")])\ndef test_delim_wrap(token, exp_res):\n    \"\"\"test func.\"\"\"\n    import buku\n\n    res = buku.delim_wrap(token)\n    assert res == exp_res\n\n\ndef test_read_in():\n    \"\"\"test func.\"\"\"\n    message = mock.Mock()\n    with mock.patch(\"buku.disable_sigint_handler\"), mock.patch(\"buku.enable_sigint_handler\"), mock.patch(\n        \"buku.input\", return_value=message\n    ):\n        import buku\n\n        res = buku.read_in(msg=mock.Mock())\n        assert res == message\n\n\ndef test_sigint_handler_with_mock():\n    \"\"\"test func.\"\"\"\n    with mock.patch(\"buku.os\") as m_os:\n        import buku\n\n        buku.sigint_handler(mock.Mock(), mock.Mock())\n        m_os._exit.assert_called_once_with(1)\n\n\ndef test_get_system_editor():\n    \"\"\"test func.\"\"\"\n    with mock.patch(\"buku.os\") as m_os:\n        import buku\n\n        res = buku.get_system_editor()\n        assert res == m_os.environ.get.return_value\n        m_os.environ.get.assert_called_once_with(\"EDITOR\", \"none\")\n\n\n@pytest.mark.parametrize(\n    \"editor, exp_res\",\n    [\n        (\"none\", False),\n        (\"0\", False),\n        (\"random_editor\", True),\n    ],\n)\ndef test_is_editor_valid(editor, exp_res):\n    \"\"\"test func.\"\"\"\n    import buku\n\n    assert buku.is_editor_valid(editor) == exp_res\n\n\n@pytest.mark.parametrize(\n    \"url, title_in, tags_in, desc\",\n    product(\n        [None, \"example.com\"],\n        [None, \"\", \"title\"],\n        [None, \"\", \"-\", \"tag1,tag2\", \",tag1,tag2,\", \",,,,,\"],\n        [None, \"\", \"-\", \"description\"],\n    ),\n)\ndef test_to_temp_file_content(url, title_in, tags_in, desc):\n    \"\"\"test func.\"\"\"\n    import buku\n\n    if desc is None:\n        desc_text = \"\\n\"\n    elif desc == \"\":\n        desc_text = \"-\"\n    else:\n        desc_text = desc\n    if title_in is None:\n        title_text = \"\"\n    elif title_in == \"\":\n        title_text = \"-\"\n    else:\n        title_text = title_in\n    res = buku.to_temp_file_content(url, title_in, tags_in, desc)\n    lines = \"\"\"# Lines beginning with \"#\" will be stripped.\n# Add URL in next line (single line).{}\n# Add TITLE in next line (single line). Leave blank to web fetch, \"-\" for no title.{}\n# Add comma-separated TAGS in next line (single line).{}\n# Add COMMENTS in next line(s). Leave blank to web fetch, \"-\" for no comments.{}\"\"\".format(\n        \"\".join([\"\\n\", url]) if url is not None else \"\",\n        \"\".join([\"\\n\", title_text]),\n        \"\".join([\"\\n\", \",\".join([x for x in tags_in.split(\",\") if x])]) if tags_in else \"\\n\",\n        \"\".join([\"\\n\", desc_text]),\n    )\n    assert res == lines\n\n\n@pytest.mark.parametrize(\n    \"content, exp_res\",\n    [\n        (\"\", None),\n        (\"#line1\\n#line2\", None),\n        (\n            \"\\n\".join(\n                [\n                    \"example.com\",\n                    \"title\",\n                    \"tags\",\n                    \"desc\",\n                ]\n            ),\n            (\"example.com\", \"title\", \",tags,\", \"desc\"),\n        ),\n    ],\n)\ndef test_parse_temp_file_content(content, exp_res):\n    \"\"\"test func.\"\"\"\n    import buku\n\n    res = buku.parse_temp_file_content(content)\n    assert res == exp_res\n\n\n@pytest.mark.skip(reason=\"can't patch subprocess\")\ndef test_edit_rec():\n    \"\"\"test func.\"\"\"\n    editor = \"nanoe\"\n    args = (\"url\", \"title_in\", \"tags_in\", \"desc\")\n    with mock.patch(\"buku.to_temp_file_content\"), mock.patch(\"buku.os\"), mock.patch(\"buku.open\"), mock.patch(\n        \"buku.parse_temp_file_content\"\n    ) as m_ptfc:\n        import buku\n\n        res = buku.edit_rec(editor, *args)\n        assert res == m_ptfc.return_value\n\n\n@pytest.mark.parametrize(\"argv, pipeargs, isatty\", product([\"argv\"], [None, []], [True, False]))\ndef test_piped_input(argv, pipeargs, isatty):\n    \"\"\"test func.\"\"\"\n    with mock.patch(\"buku.sys\") as m_sys:\n        m_sys.stdin.isatty.return_value = isatty\n        m_sys.stdin.readlines.return_value = \"arg1\\narg2\"\n        import buku\n\n        if pipeargs is None and not isatty:\n            with pytest.raises(TypeError):\n                buku.piped_input(argv, pipeargs)\n            return\n        buku.piped_input(argv, pipeargs)\n\n\nclass TestHelpers(unittest.TestCase):\n\n    # @unittest.skip('skipping')\n    # @unittest.skip('skipping')\n    def test_is_int(self):\n        self.assertTrue(is_int(\"0\"))\n        self.assertTrue(is_int(\"1\"))\n        self.assertTrue(is_int(\"-1\"))\n        self.assertFalse(is_int(\"\"))\n        self.assertFalse(is_int(\"one\"))\n\n\n# This test fails because we use os._exit() now\n@unittest.skip(\"skipping\")\ndef test_sigint_handler(capsys):\n    try:\n        # sending SIGINT to self\n        os.kill(os.getpid(), signal.SIGINT)\n    except SystemExit as error:\n        out, err = capsys.readouterr()\n        # assert exited with 1\n        assert error.args[0] == 1\n        # assert proper error message\n        assert out == \"\"\n        assert err == \"\\nInterrupted.\\n\"\n\n\n@pytest.mark.vcr(\"tests/vcr_cassettes/test_fetch_data_with_url.yaml\")\n@pytest.mark.parametrize(\n    \"url, exp_res\",\n    [\n        [\"http://example.com.\", {'bad': True}],\n        [\"http://example.com\", {'title': 'Example Domain', 'fetch_status': 200}],\n        [\"http://example.com/page1.txt\", {'mime': True, 'fetch_status': 404}],\n        [\"about:new_page\", {'bad': True}],\n        [\"chrome://version/\", {'bad': True}],\n        [\"chrome://version/\", {'bad': True}],\n        # [\n        #     'http://4pda.ru/forum/index.php?showtopic=182463&st=1640#entry6044923',\n        #     {'title': 'Samsung GT-I5800 Galaxy 580 - Обсуждение - 4PDA',\n        #      'desc':  'Samsung GT-I5800 Galaxy 580 - Обсуждение - 4PDA',\n        #      'fetch_status': 200},\n        # ],\n        [\n            \"https://www.google.ru/search?\"\n            \"newwindow=1&safe=off&q=xkbcomp+alt+gr&\"\n            \"oq=xkbcomp+alt+gr&\"\n            \"gs_l=serp.3..33i21.28976559.28977886.0.\"\n            \"28978017.6.6.0.0.0.0.167.668.0j5.5.0....0...1c.1.64.\"\n            \"serp..1.2.311.06cSKPTLo18\",\n            {'title': 'xkbcomp alt gr', 'fetch_status': 200},\n        ],\n        [\n            \"http://www.vim.org/scripts/script.php?script_id=4641\",\n            {'title': 'mlessnau_case - \"in-case\" selection, deletion and substitution for underscore, camel, mixed case : vim online',\n             'fetch_status': 200},\n        ],\n    ],\n    ids=lambda s: (s.split('?')[0] + '~' if isinstance(s, str) and '?' in s else None),\n)\ndef test_fetch_data_with_url(url, exp_res):\n    \"\"\"test func.\"\"\"\n    import urllib3\n\n    import buku\n\n    buku.urllib3 = urllib3\n    buku.myproxy = None\n    res = buku.fetch_data(url)\n    if urlparse(url).netloc == \"www.google.ru\":\n        res = res._replace(title=res.title.split(' - ')[0])\n    assert res == FetchResult(url, **exp_res)\n\n\n@pytest.mark.parametrize(\n    \"url, exp_res\",\n    [\n        (\"http://example.com\", False),\n        (\"apt:package1,package2,package3\", True),\n        (\"apt://firefox\", True),\n        (\"file:///tmp/vim-markdown-preview.html\", True),\n        (\"place:sort=8&maxResults=10\", True),\n    ],\n)\ndef test_is_nongeneric_url(url, exp_res):\n    import buku\n\n    res = buku.is_nongeneric_url(url)\n    assert res == exp_res\n\n\n@pytest.mark.parametrize('tags, newtag, exp_tags', [\n    ('', None, ','), ('', 'new tag', ',new tag,'),\n    ('foo, bar, baz', None, ',bar,baz,foo,'),\n    ('foo, bar, baz', 'new tag', ',bar,baz,foo,new tag,'),\n])\n@pytest.mark.parametrize('title', ['Bookmark title', '', None])\n@pytest.mark.parametrize('url', ['http://example.com', 'javascript:void(0)', ''])\ndef test_import_md(tmpdir, url, title, tags, newtag, exp_tags):\n    from buku import import_md\n\n    p = tmpdir.mkdir(\"importmd\").join(\"test.md\")\n    print(line := (f'<{url}>' if title is None else f'[{title}]({url})') +\n                  ('' if not tags else f' <!-- TAGS: {tags} -->'))\n    p.write(line)\n    res = list(import_md(p.strpath, newtag))\n    assert res == ([] if not url else  # `<>` and `[title]()` are not valid\n                   [(url, title or '', exp_tags, None, 0, True, False)])\n\n@pytest.mark.parametrize('newtag, exp_res', [\n    (None, ('http://example.com', 'text1', ',', None, 0, True, False)),\n    ('tag1', ('http://example.com', 'text1', ',tag1,', None, 0, True, False)),\n])\n@pytest.mark.parametrize('extension', ['.rss', '.atom'])\ndef test_import_rss(tmpdir, extension, newtag, exp_res):\n    from buku import import_rss\n\n    p = tmpdir.mkdir('importrss').join('test' + extension)\n    p.write(\n        '<feed xmlns=\"http://www.w3.org/2005/Atom\">\\n'\n        '    <title>Bookmarks</title>\\n'\n        '    <generator uri=\"https://github.com/jarun/buku\">buku</generator>\\n'\n        '    <entry>\\n'\n        '        <title>text1</title>\\n'\n        '        <link href=\"http://example.com\"/>\\n'\n        '    </entry>\\n'\n        '</feed>\\n')\n    res = list(import_rss(p.strpath, newtag))\n    assert res[0] == exp_res\n\n@pytest.mark.parametrize('tags, newtag, exp_tags', [\n    ('', None, ','), ('', 'new tag', ',new tag,'),\n    ('tag1: ::tag2:tag::3:tag4:: :tag:::5: ta g::6:: ', None, ',tag1,:tag2,tag:3,tag4:,tag::5,ta g:6:,'),\n    ('tag1: ::tag2:tag::3:tag4:: :tag:::5: ta g::6:: ', 'new tag', ',new tag,tag1,:tag2,tag:3,tag4:,tag::5,ta g:6:,'),\n])\n@pytest.mark.parametrize('title', ['Bookmark title', '', None])\n@pytest.mark.parametrize('url', ['http://example.com', 'javascript:void(0)', ''])\ndef test_import_org(tmpdir, url, title, tags, newtag, exp_tags):\n    from buku import import_org\n\n    p = tmpdir.mkdir(\"importorg\").join(\"test.org\")\n    print(line := (f'[[{url}]]' if title is None else f'[[{url}][{title}]]') +\n                  ('' if not tags else f' :{tags}:'))\n    p.write(line)\n    res = list(import_org(p.strpath, newtag))\n    assert res == ([] if not url or title == '' else  # `[[]]`, `[[][title]]` and `[[url][]]` are not valid\n                   [(url, title or '', exp_tags, None, 0, True, False)])\n\n\n@pytest.mark.parametrize(\n    \"html_text, exp_res\",\n    [\n        (\n            \"\"\"<DT><A HREF=\"https://github.com/j\" ADD_DATE=\"1360951967\" PRIVATE=\"1\" TAGS=\"tag1,tag2\">GitHub</A>\n<DD>comment for the bookmark here\n<a> </a>\"\"\",\n            (\n                (\n                    \"https://github.com/j\",\n                    \"GitHub\",\n                    \",tag1,tag2,\",\n                    \"comment for the bookmark here\",\n                    0,\n                    True,\n                    False,\n                ),\n            ),\n        ),\n        (\n            \"\"\"DT><A HREF=\"https://github.com/j\" ADD_DATE=\"1360951967\" PRIVATE=\"1\" TAGS=\"tag1,tag2\">GitHub</A>\n            <DD>comment for the bookmark here\n            <a>second line of the comment here</a>\"\"\",\n            (\n                (\n                    \"https://github.com/j\",\n                    \"GitHub\",\n                    \",tag1,tag2,\",\n                    \"comment for the bookmark here\",\n                    0,\n                    True,\n                    False,\n                ),\n            ),\n        ),\n        (\n            \"\"\"DT><A HREF=\"https://github.com/j\" ADD_DATE=\"1360951967\" PRIVATE=\"1\" TAGS=\"tag1,tag2\">GitHub</A>\n            <DD>comment for the bookmark here\n            second line of the comment here\n            third line of the comment here\n            <DT><A HREF=\"https://news.com/\" ADD_DATE=\"1360951967\" PRIVATE=\"1\" TAGS=\"tag1,tag2,tag3\">News</A>\"\"\",\n            (\n                (\n                    \"https://github.com/j\",\n                    \"GitHub\",\n                    \",tag1,tag2,\",\n                    \"comment for the bookmark here\\n            \"\n                    \"second line of the comment here\\n            \"\n                    \"third line of the comment here\",\n                    0,\n                    True,\n                    False,\n                ),\n                (\"https://news.com/\", \"News\", \",tag1,tag2,tag3,\", None, 0, True, False),\n            ),\n        ),\n        (\n            \"\"\"DT><A HREF=\"https://github.com/j\" ADD_DATE=\"1360951967\" PRIVATE=\"1\" TAGS=\"tag1,tag2\">GitHub</A>\n            <DD>comment for the bookmark here\"\"\",\n            (\n                (\n                    \"https://github.com/j\",\n                    \"GitHub\",\n                    \",tag1,tag2,\",\n                    \"comment for the bookmark here\",\n                    0,\n                    True,\n                    False,\n                ),\n            ),\n        ),\n    ],\n)\ndef test_import_html(html_text, exp_res):\n    \"\"\"test method.\"\"\"\n    from bs4 import BeautifulSoup\n\n    from buku import import_html\n\n    html_soup = BeautifulSoup(html_text, \"html.parser\")\n    res = list(import_html(html_soup, False, None))\n    for item, exp_item in zip(res, exp_res):\n        assert item == exp_item, \"Actual item:\\n{}\".format(item)\n\n\ndef test_import_html_and_add_parent():\n    from bs4 import BeautifulSoup\n\n    from buku import import_html\n\n    html_text = \"\"\"<DT><H3>1s (blah,blah)</H3>\n<DL><p>\n<DT><A HREF=\"http://example.com/\"></A>\"\"\"\n    exp_res = (\"http://example.com/\", None, \",1s (blah blah),\", None, 0, True, False)\n    html_soup = BeautifulSoup(html_text, \"html.parser\")\n    res = list(import_html(html_soup, True, None))\n    assert res[0] == exp_res\n\n\n@pytest.mark.parametrize(\n    \"add_all_parent, exp_res\",\n    [\n        (\n            True,\n            [\n                (\"http://example11.com\", None, \",folder11,\", None, 0, True, False),\n                (\n                    \"http://example12.com\",\n                    None,\n                    \",folder11,folder12,\",\n                    None,\n                    0,\n                    True,\n                    False,\n                ),\n                (\n                    \"http://example13.com\",\n                    None,\n                    \",folder11,folder12,folder13 (blah blah),tag3,tag4,\",\n                    None,\n                    0,\n                    True,\n                    False,\n                ),\n                (\n                    \"http://example121.com\",\n                    None,\n                    \",folder11,folder12,folder121,\",\n                    None,\n                    0,\n                    True,\n                    False,\n                ),\n            ],\n        ),\n        (\n            False,\n            [\n                (\"http://example11.com\", None, \",folder11,\", None, 0, True, False),\n                (\"http://example121.com\", None, \",folder121,\", None, 0, True, False),\n                (\"http://example12.com\", None, \",folder12,\", None, 0, True, False),\n                (\"http://example13.com\", None, \",folder13 (blah blah),tag3,tag4,\", None, 0, True, False),\n            ],\n        ),\n    ],\n)\ndef test_import_html_and_add_all_parent(add_all_parent, exp_res):\n    from bs4 import BeautifulSoup\n\n    from buku import import_html\n\n    html_text = \"\"\"\n<DL><p>\n<DT><H3>Folder01</H3><DL><p>\n    <DT><A HREF=\"http://example01.com\"></A></DT>\n    <DT><H3>Folder02</H3><DL><p>\n        <DT><A HREF=\"http://example02.com\"></A></DT>\n        <DT><H3>Folder03</H3><DL><p>\n            <DT><A HREF=\"http://example03.com\" TAGS=\"tag1,tag2\"></A></DT>\n        </DL><p></DT>\n    </DL><p></DT>\n</DL><p></DT>\n<DT><H3>Folder11</H3><DL><p>\n    <DT><A HREF=\"http://example11.com\"></A></DT>\n    <DT><H3>Folder12</H3><DL><p>\n        <DT><H3>Folder121</H3><DL><p>\n            <DT><A HREF=\"http://example121.com\"></A></DT>\n        </DL><p></DT>\n        <DT><A HREF=\"http://example12.com\"></A></DT>\n        <DT><H3>Folder13 (blah,blah)</H3><DL><p>\n            <DT><A HREF=\"http://example13.com\" TAGS=\"tag3,tag4\"></A></DT>\n        </DL><p></DT>\n    </DL><p></DT>\n</DL><p></DT></DL>\n\"\"\"\n    html_soup = BeautifulSoup(html_text, \"html.parser\")\n    res = list(import_html(html_soup, True, None, add_all_parent))  # pylint: disable=E1121\n    assert check_import_html_results_contains(res, exp_res)\n\n\ndef test_import_html_and_new_tag():\n    from bs4 import BeautifulSoup\n\n    from buku import import_html\n\n    html_text = \"\"\"<DT><A HREF=\"https://github.com/j\" TAGS=\"tag1,tag2\">GitHub</A>\n<DD>comment for the bookmark here\"\"\"\n    exp_res = (\n        \"https://github.com/j\",\n        \"GitHub\",\n        \",tag1,tag2,tag3,\",\n        \"comment for the bookmark here\",\n        0,\n        True,\n        False,\n    )\n    html_soup = BeautifulSoup(html_text, \"html.parser\")\n    res = list(import_html(html_soup, False, \"tag3\"))\n    assert res[0] == exp_res\n\n\n@pytest.mark.parametrize('profiles, expected', [\n    (dedent('''\n    [Profile3]\n    Name=ABCD\n    IsRelative=0\n    Path=/path/to/removable/drive/ABCD\n    Default=1\n\n    [Install4F96D1932A9F858E]\n    Default=/path/to/custom/path/my-main-profile\n    Locked=1\n\n    [Profile1]\n    Name=Main\n    IsRelative=0\n    Path=/path/to/custom/path/main-profile\n\n    [Profile0]\n    Name=default\n    IsRelative=1\n    Path=zsq8tck1.default-release\n\n    [InstallD087BC9767A4CB84]\n    Default=1koqf71l.default-nightly\n    Locked=1\n\n    [General]\n    StartWithLastProfile=1\n    Version=2\n    '''), ['/path/to/custom/path/my-main-profile', '1koqf71l.default-nightly']),\n    (dedent('''\n    [Profile3]\n    Name=ABCD\n    IsRelative=0\n    Path=/path/to/removable/drive/ABCD\n    Default=1\n\n    [Profile1]\n    Name=Main\n    IsRelative=0\n    Path=/path/to/custom/path/my-main-profile\n\n    [Profile0]\n    Name=default\n    IsRelative=1\n    Path=zsq8tck1.default-release\n\n    [General]\n    StartWithLastProfile=1\n    Version=2\n    '''), ['/path/to/removable/drive/ABCD', 'zsq8tck1.default-release']),\n    ('', []), (None, []),\n])\n@mock.patch('os.path.exists')\ndef test_get_firefox_profile_names(_os_path_exists, profiles, expected):\n    _os_path_exists.return_value = profiles is not None\n    with mock.patch.object(ConfigParser, 'read', lambda self, _: self.read_string(profiles)):\n        import buku\n        assert buku.get_firefox_profile_names('') == expected\n\n@pytest.mark.parametrize('profiles, specified, expected', [\n    (['foo', '/bar/baz'], None, {\n        'foo': os.path.join('~/profiles', 'foo', 'places.sqlite'),\n        '/bar/baz': os.path.join('/bar/baz', 'places.sqlite'),\n    }),\n    (['foo', '/bar/baz'], 'qux', {'qux': os.path.join('~/profiles', 'qux', 'places.sqlite')}),\n    ([], '/grue/xyzzy', {'/grue/xyzzy': os.path.join('/grue/xyzzy', 'places.sqlite')}),\n])\ndef test_get_firefox_db_paths(profiles, specified, expected):\n    with mock.patch('buku.get_firefox_profile_names', return_value=profiles):\n        import buku\n        assert buku.get_firefox_db_paths('~/profiles', specified) == expected\n\n\n@pytest.mark.parametrize(\n    \"platform, params\",\n    [\n        [\"linux\", [\"xsel\", \"-b\", \"-i\"]],\n        [\"freebsd\", [\"xsel\", \"-b\", \"-i\"]],\n        [\"openbsd\", [\"xsel\", \"-b\", \"-i\"]],\n        [\"darwin\", [\"pbcopy\"]],\n        [\"win32\", [\"clip\"]],\n        [\"random\", None],\n    ],\n)\ndef test_copy_to_clipboard(platform, params):\n    # m_popen = mock.Mock()\n    content = mock.Mock()\n    m_popen_retval = mock.Mock()\n    platform_recognized = platform.startswith((\"linux\", \"freebsd\", \"openbsd\")) or platform in (\"darwin\", \"win32\")\n    with mock.patch(\"buku.sys\") as m_sys, mock.patch(\"buku.Popen\", return_value=m_popen_retval) as m_popen, mock.patch(\n        \"buku.shutil.which\", return_value=True\n    ):\n        m_sys.platform = platform\n        import subprocess\n\n        from buku import copy_to_clipboard\n\n        copy_to_clipboard(content)\n        if platform_recognized:\n            m_popen.assert_called_once_with(\n                params,\n                stdin=subprocess.PIPE,\n                stdout=subprocess.DEVNULL,\n                stderr=subprocess.DEVNULL,\n            )\n            m_popen_retval.communicate.assert_called_once_with(content)\n        else:\n            logging.info(\"popen is called {} on unrecognized platform\".format(m_popen.call_count))\n\n\n@pytest.mark.parametrize(\n    \"export_type, exp_res\",\n    [\n        [\"random\", None],\n        [\n            'html',\n            '<!DOCTYPE NETSCAPE-Bookmark-file-1>\\n\\n'\n            '<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\">\\n'\n            '<TITLE>Bookmarks</TITLE>\\n<H1>Bookmarks</H1>\\n\\n<DL><p>\\n'\n            '    <DT><H3 ADD_DATE=\"1556430615\" LAST_MODIFIED=\"1556430615\" PERSONAL_TOOLBAR_FOLDER=\"true\">buku bookmarks</H3>\\n'\n            '    <DL><p>\\n'\n            '        <DT><A HREF=\"http://example.com\" ADD_DATE=\"1556430615\" LAST_MODIFIED=\"1556430615\"></A>\\n'\n            '        <DT><A HREF=\"http://example.org\" ADD_DATE=\"1556430615\" LAST_MODIFIED=\"1556430615\" TAGS=\"bar,baz,foo\"></A>\\n'\n            '        <DT><A HREF=\"http://google.com\" ADD_DATE=\"1556430615\" LAST_MODIFIED=\"1556430615\" TAGS=\"bar,baz,foo\">Google</A>\\n'\n            '    </DL><p>\\n</DL><p>',\n        ],\n        [\n            'org',\n            '* [[http://example.com]]\\n'\n            '* [[http://example.org]] :bar:baz:foo:\\n'\n            '* [[http://google.com][Google]] :bar:baz:foo:\\n',\n        ],\n        [\n            'markdown',\n            '- <http://example.com>\\n'\n            '- <http://example.org> <!-- TAGS: bar,baz,foo -->\\n'\n            '- [Google](http://google.com) <!-- TAGS: bar,baz,foo -->\\n',\n        ],\n        [\n            'rss',\n            '<feed xmlns=\"http://www.w3.org/2005/Atom\">\\n'\n            '    <title>Bookmarks</title>\\n'\n            '    <generator uri=\"https://github.com/jarun/buku\">buku</generator>\\n'\n            '    <entry>\\n'\n            '        <title></title>\\n'\n            '        <link href=\"http://example.com\" rel=\"alternate\" type=\"text/html\"/>\\n'\n            '        <id>1</id>\\n'\n            '    </entry>\\n'\n            '    <entry>\\n'\n            '        <title></title>\\n'\n            '        <link href=\"http://example.org\" rel=\"alternate\" type=\"text/html\"/>\\n'\n            '        <id>2</id>\\n'\n            '        <category term=\"bar\"/>\\n'\n            '        <category term=\"baz\"/>\\n'\n            '        <category term=\"foo\"/>\\n'\n            '    </entry>\\n'\n            '    <entry>\\n'\n            '        <title>Google</title>\\n'\n            '        <link href=\"http://google.com\" rel=\"alternate\" type=\"text/html\"/>\\n'\n            '        <id>3</id>\\n'\n            '        <category term=\"bar\"/>\\n'\n            '        <category term=\"baz\"/>\\n'\n            '        <category term=\"foo\"/>\\n'\n            '    </entry>\\n'\n            '</feed>',\n        ],\n        [\n            'xbel',\n            '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n'\n            '<!DOCTYPE xbel PUBLIC \"+//IDN python.org//DTD XML Bookmark Exchange Language 1.0//EN//XML\"'\n            ' \"http://pyxml.sourceforge.net/topics/dtds/xbel.dtd\">\\n\\n'\n            '<xbel version=\"1.0\">\\n'\n            '    <bookmark href=\"http://example.com\">\\n'\n            '        <title></title>\\n'\n            '    </bookmark>\\n'\n            '    <bookmark href=\"http://example.org\" TAGS=\"bar,baz,foo\">\\n'\n            '        <title></title>\\n'\n            '    </bookmark>\\n'\n            '    <bookmark href=\"http://google.com\" TAGS=\"bar,baz,foo\">\\n'\n            '        <title>Google</title>\\n'\n            '    </bookmark>\\n'\n            '</xbel>'\n        ],\n    ],\n)\ndef test_convert_bookmark_set(export_type, exp_res, monkeypatch):\n    import buku\n    from buku import convert_bookmark_set\n\n    bms = [\n        (1, \"http://example.com\", \"\", \",\", \"\", 0),\n        (2, \"http://example.org\", None, \",bar,baz,foo,\", \"\", 0),\n        (3, \"http://google.com\", \"Google\", \",bar,baz,foo,\", \"\", 0),\n    ]\n    if export_type == \"random\":\n        with pytest.raises(AssertionError):\n            convert_bookmark_set(bms, export_type=export_type)\n    else:\n\n        def return_fixed_number():\n            return 1556430615\n\n        monkeypatch.setattr(buku.time, \"time\", return_fixed_number)\n        res = convert_bookmark_set(bms, export_type=export_type)\n        assert res[\"count\"] == 3\n        assert exp_res == res[\"data\"]\n\n\n@pytest.mark.parametrize(\n    \"tags,data\",\n    [\n        [\",\", \"\\n\"],\n        [\",tag1,tag2,\", \" :tag1:tag2:\\n\"],\n        [\",word1 word2,\", \" :word1_word2:\\n\"],\n        [\",word1:word2,\", \" :word1_word2:\\n\"],\n        [\",##tag##,\", \" :_tag_:\\n\"],\n        [\",##tag##,!!tag!!,\", \" :_tag_:\\n\"],\n        [\",home / personal,\", \" :home_personal:\\n\"],\n    ],\n)\ndef test_convert_tags_to_org_mode_tags(tags, data):\n    from buku import convert_tags_to_org_mode_tags\n\n    res = convert_tags_to_org_mode_tags(tags)\n    assert res == data\n\n\n@pytest.mark.parametrize('charset', ['ISO-8859-1', 'UTF-8'])\n@pytest.mark.parametrize('mode', ['charset', 'content', 'header'])\ndef test_get_data_from_page(charset, mode):\n    from urllib3.response import HTTPResponse\n    from buku import get_data_from_page\n    title = 'Répertoire des articles relatifs à l\\'Asiminier - Asimina triloba (L.) Dunal (site Les Fruitiers Rares)'\n    headers = (None if mode != 'header' else {'Content-Type': f'text/html; charset={charset}'})\n    meta = {\n        'charset': f'\\n<meta charset=\"{charset}\"/>',\n        'content': f'\\n<meta http-equiv=\"content-type\" content=\"text/html; charset={charset}\"/>',\n    }.get(mode, '')\n    keywords = '<meta name=\"keywords\" content=\"foo, bar   baz, quux\"/>'\n    body = f'<html>\\n\\n<head>{meta}\\n{keywords}\\n<title>{title}</title>\\n</head>\\n<body></body>\\n\\n</html>\\n'\n    resp = HTTPResponse(body.encode(charset), headers)\n    parsed_title, desc, tags = get_data_from_page(resp)\n    assert (parsed_title, tags) == (title, \"foo,bar baz,quux\")\n\n\n@pytest.mark.parametrize('tokens, kwargs, expected', [\n    (None, {}, None),\n    ('404', {}, {404}),\n    ('403,404', {}, {403, 404}),\n    ({'400', '500'}, {}, {400, 500}),\n    (('400-404', '500'), {}, {400, 401, 402, 403, 404, 500}),\n    (['400-404', '500'], {'valid': lambda x: x in range(400, 600)}, {400, 401, 402, 403, 404, 500}),\n    (['400-404', '300'], {'valid': lambda x: x in range(400, 600)}, ValueError('Not a valid range')),\n    ('-3', {}, {-3}),\n    ('-3', {'maxidx': 10}, {8, 9, 10}),\n    ('-30', {'maxidx': 3}, {1, 2, 3}),\n    ('10-3', {'maxidx': 5}, {3, 4, 5}),\n])\ndef test_parse_range(tokens, kwargs, expected):\n    if not isinstance(expected, Exception):\n        assert parse_range(tokens, **kwargs) == expected\n    else:\n        try:\n            parse_range(tokens, **kwargs)\n            assert False, 'error expected'\n        except Exception as e:\n            assert type(e) is type(expected)\n            assert str(e) == str(expected)\n\n\ndef test_split_by_marker():\n    search_string = (' global substring  .title substring :url substring :https '\n                     '> description substring #partial,tags: #,exact,tags, *another global substring ')\n    assert split_by_marker(search_string) == [\n        ' global substring', '.title substring', ':url substring', ':https',\n        '> description substring', '#partial,tags:', '#,exact,tags,', '*another global substring ',\n    ]\n\n\ndef test_SortKey():\n    assert repr(SortKey('foo', ascending=True)) == \"+'foo'\"\n    assert repr(SortKey('bar', ascending=False)) == \"-'bar'\"\n    assert SortKey('foo', ascending=True) > SortKey('bar', ascending=True)\n    assert not SortKey('foo', ascending=True) > SortKey('foo', ascending=True)  # pylint: disable=unnecessary-negation\n    assert not SortKey('foo', ascending=True) < SortKey('foo', ascending=True)  # pylint: disable=unnecessary-negation\n    assert not SortKey('foo', ascending=True) < SortKey('bar', ascending=True)  # pylint: disable=unnecessary-negation\n    assert SortKey('foo', ascending=False) < SortKey('bar', ascending=False)\n    assert not SortKey('foo', ascending=False) < SortKey('foo', ascending=False)  # pylint: disable=unnecessary-negation\n    assert not SortKey('foo', ascending=False) > SortKey('foo', ascending=False)  # pylint: disable=unnecessary-negation\n    assert not SortKey('foo', ascending=False) > SortKey('bar', ascending=False)  # pylint: disable=unnecessary-negation\n\n    custom_order = lambda s: (SortKey(len(s), ascending=False), SortKey(s, ascending=True))\n    assert sorted(['foo', 'bar', 'baz', 'quux'], key=custom_order) == ['quux', 'bar', 'baz', 'foo']\n"
  },
  {
    "path": "tests/test_bukuDb/25491522_res.yaml",
    "content": "? !!python/tuple ['http://voyagerlive.org/', Voyager, ',bookmarks bar,', null, 0,\n  true, false]\n: {}\n? !!python/tuple ['http://wiki.ubuntu.com/', Ubuntu Wiki (community-edited website),\n  ',bookmarks bar,imported from firefox (2011-09-02  06:03:50),ubuntu and free software links,', null, 0,\n  true, false]\n: {}\n? !!python/tuple ['http://www.debian.org/', Debian (Ubuntu is based on Debian), ',bookmarks\n    bar,imported from firefox (2011-09-02  06:03:50),ubuntu and free software links,', null, 0, true, false]\n: {}\n? !!python/tuple ['http://www.ubuntu.com/', Ubuntu, ',bookmarks bar,imported from\n    firefox (2011-09-02  06:03:50),ubuntu and free software links,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://addons.mozilla.org/fr/firefox/addon/adblock-plus/', Adblock\n    Plus, ',bookmarks bar,f+,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://addons.mozilla.org/fr/firefox/addon/searchpreview/', SearchPreview,\n  ',bookmarks bar,f+,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://addons.mozilla.org/fr/firefox/language-tools/', Language\n    Tools, ',bookmarks bar,f+,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://answers.launchpad.net/ubuntu/+addquestion', Make a Support\n    Request to the Ubuntu Community, ',bookmarks bar,imported from firefox (2011-09-02  06:03:50),ubuntu\n    and free software links,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://one.ubuntu.com/', Ubuntu One - The personal cloud that\n    brings your digital life together, ',bookmarks bar,imported from firefox (2011-09-02  06:03:50),ubuntu\n    and free software links,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://www.google.com/', Google, ',other bookmarks,', null, 0,\n  true, false]\n: {}\n? !!python/tuple ['https://www.mozilla.org/en-US/about/', About Us, ',bookmarks bar,imported\n    from firefox (2011-09-02  06:03:50),mozilla firefox,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://www.mozilla.org/en-US/contribute/', Get Involved, ',bookmarks\n    bar,imported from firefox (2011-09-02  06:03:50),mozilla firefox,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://www.mozilla.org/en-US/firefox/customize/', Customize Firefox,\n  ',bookmarks bar,imported from firefox (2011-09-02  06:03:50),mozilla firefox,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://www.mozilla.org/en-US/firefox/help/', Help and Tutorials,\n  ',bookmarks bar,imported from firefox (2011-09-02  06:03:50),mozilla firefox,', null, 0, true, false]\n: {}\n"
  },
  {
    "path": "tests/test_bukuDb/25491522_res_nopt.yaml",
    "content": "? !!python/tuple ['http://voyagerlive.org/', Voyager, ',', null, 0, true, false]\n: {}\n? !!python/tuple ['http://wiki.ubuntu.com/', Ubuntu Wiki (community-edited website),\n  ',', null, 0, true, false]\n: {}\n? !!python/tuple ['http://www.debian.org/', Debian (Ubuntu is based on Debian), ',',\n  null, 0, true, false]\n: {}\n? !!python/tuple ['http://www.ubuntu.com/', Ubuntu, ',', null, 0, true, false]\n: {}\n? !!python/tuple ['https://addons.mozilla.org/fr/firefox/addon/adblock-plus/', Adblock\n    Plus, ',', null, 0, true, false]\n: {}\n? !!python/tuple ['https://addons.mozilla.org/fr/firefox/addon/searchpreview/', SearchPreview,\n  ',', null, 0, true, false]\n: {}\n? !!python/tuple ['https://addons.mozilla.org/fr/firefox/language-tools/', Language\n    Tools, ',', null, 0, true, false]\n: {}\n? !!python/tuple ['https://answers.launchpad.net/ubuntu/+addquestion', Make a Support\n    Request to the Ubuntu Community, ',', null, 0, true, false]\n: {}\n? !!python/tuple ['https://one.ubuntu.com/', Ubuntu One - The personal cloud that\n    brings your digital life together, ',', null, 0, true, false]\n: {}\n? !!python/tuple ['https://www.google.com/', Google, ',', null, 0, true, false]\n: {}\n? !!python/tuple ['https://www.mozilla.org/en-US/about/', About Us, ',', null, 0,\n  true, false]\n: {}\n? !!python/tuple ['https://www.mozilla.org/en-US/contribute/', Get Involved, ',',\n  null, 0, true, false]\n: {}\n? !!python/tuple ['https://www.mozilla.org/en-US/firefox/customize/', Customize Firefox,\n  ',', null, 0, true, false]\n: {}\n? !!python/tuple ['https://www.mozilla.org/en-US/firefox/help/', Help and Tutorials,\n  ',', null, 0, true, false]\n: {}\n"
  },
  {
    "path": "tests/test_bukuDb/Bookmarks",
    "content": "{\n   \"checksum\": \"159ce0a29c234510ba09c3091e0b513b\",\n   \"roots\": {\n      \"bookmark_bar\": {\n         \"children\": [ {\n            \"date_added\": \"13084680099000000\",\n            \"id\": \"6\",\n            \"name\": \"Voyager\",\n            \"type\": \"url\",\n            \"url\": \"http://voyagerlive.org/\"\n         }, {\n            \"children\": [ {\n               \"date_added\": \"13084680167000000\",\n               \"id\": \"8\",\n               \"name\": \"0\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/0/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"9\",\n               \"name\": \"1\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/1/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"10\",\n               \"name\": \"2\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/2/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"11\",\n               \"name\": \"3\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/3/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"12\",\n               \"name\": \"4\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/4/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"13\",\n               \"name\": \"5\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/5/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"14\",\n               \"name\": \"6\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/6/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"15\",\n               \"name\": \"7\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/7/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"16\",\n               \"name\": \"8\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/8/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"17\",\n               \"name\": \"9\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/9/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"18\",\n               \"name\": \"10\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/10/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"19\",\n               \"name\": \"11\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/11/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"20\",\n               \"name\": \"12\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/12/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"21\",\n               \"name\": \"13\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/13/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"22\",\n               \"name\": \"14\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/14/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"23\",\n               \"name\": \"15\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/15/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"24\",\n               \"name\": \"16\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/16/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"25\",\n               \"name\": \"17\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/17/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"26\",\n               \"name\": \"18\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/18/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"27\",\n               \"name\": \"19\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/19/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"28\",\n               \"name\": \"20\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/20/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"29\",\n               \"name\": \"21\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/21/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"30\",\n               \"name\": \"22\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/22/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"31\",\n               \"name\": \"23\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/23/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"32\",\n               \"name\": \"24\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/24/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"33\",\n               \"name\": \"25\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/25/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"34\",\n               \"name\": \"26\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/26/index.html\"\n            }, {\n               \"date_added\": \"13084680271000000\",\n               \"id\": \"35\",\n               \"name\": \"27\",\n               \"type\": \"url\",\n               \"url\": \"file:///.startpage/27/index.html\"\n            } ],\n            \"date_added\": \"13149362306499266\",\n            \"date_modified\": \"0\",\n            \"id\": \"7\",\n            \"name\": \"SP\",\n            \"type\": \"folder\"\n         }, {\n            \"children\": [ {\n               \"date_added\": \"13084680637000000\",\n               \"id\": \"37\",\n               \"name\": \"Flash Install\",\n               \"type\": \"url\",\n               \"url\": \"apt://flashplugin-installer\"\n            }, {\n               \"date_added\": \"13084680669000000\",\n               \"id\": \"38\",\n               \"name\": \"Adblock Plus\",\n               \"type\": \"url\",\n               \"url\": \"https://addons.mozilla.org/fr/firefox/addon/adblock-plus/\"\n            }, {\n               \"date_added\": \"13084680688000000\",\n               \"id\": \"39\",\n               \"name\": \"SearchPreview\",\n               \"type\": \"url\",\n               \"url\": \"https://addons.mozilla.org/fr/firefox/addon/searchpreview/\"\n            }, {\n               \"date_added\": \"13084680713000000\",\n               \"id\": \"40\",\n               \"name\": \"Language Tools\",\n               \"type\": \"url\",\n               \"url\": \"https://addons.mozilla.org/fr/firefox/language-tools/\"\n            } ],\n            \"date_added\": \"13149362306505611\",\n            \"date_modified\": \"0\",\n            \"id\": \"36\",\n            \"name\": \"F+\",\n            \"type\": \"folder\"\n         }, {\n            \"children\": [ {\n               \"children\": [ {\n                  \"date_added\": \"13084680036000000\",\n                  \"id\": \"43\",\n                  \"name\": \"Ubuntu\",\n                  \"type\": \"url\",\n                  \"url\": \"http://www.ubuntu.com/\"\n               }, {\n                  \"date_added\": \"13084680036000000\",\n                  \"id\": \"44\",\n                  \"name\": \"Ubuntu Wiki (community-edited website)\",\n                  \"type\": \"url\",\n                  \"url\": \"http://wiki.ubuntu.com/\"\n               }, {\n                  \"date_added\": \"13084680036000000\",\n                  \"id\": \"45\",\n                  \"name\": \"Make a Support Request to the Ubuntu Community\",\n                  \"type\": \"url\",\n                  \"url\": \"https://answers.launchpad.net/ubuntu/+addquestion\"\n               }, {\n                  \"date_added\": \"13084680036000000\",\n                  \"id\": \"46\",\n                  \"name\": \"Debian (Ubuntu is based on Debian)\",\n                  \"type\": \"url\",\n                  \"url\": \"http://www.debian.org/\"\n               }, {\n                  \"date_added\": \"13084680036000000\",\n                  \"id\": \"47\",\n                  \"name\": \"Ubuntu One - The personal cloud that brings your digital life together\",\n                  \"type\": \"url\",\n                  \"url\": \"https://one.ubuntu.com/\"\n               } ],\n               \"date_added\": \"13149362306508801\",\n               \"date_modified\": \"0\",\n               \"id\": \"42\",\n               \"name\": \"Ubuntu and Free Software links\",\n               \"type\": \"folder\"\n            }, {\n               \"children\": [ {\n                  \"date_added\": \"13084680036000000\",\n                  \"id\": \"49\",\n                  \"name\": \"Help and Tutorials\",\n                  \"type\": \"url\",\n                  \"url\": \"https://www.mozilla.org/en-US/firefox/help/\"\n               }, {\n                  \"date_added\": \"13084680036000000\",\n                  \"id\": \"50\",\n                  \"name\": \"Customize Firefox\",\n                  \"type\": \"url\",\n                  \"url\": \"https://www.mozilla.org/en-US/firefox/customize/\"\n               }, {\n                  \"date_added\": \"13084680036000000\",\n                  \"id\": \"51\",\n                  \"name\": \"Get Involved\",\n                  \"type\": \"url\",\n                  \"url\": \"https://www.mozilla.org/en-US/contribute/\"\n               }, {\n                  \"date_added\": \"13084680036000000\",\n                  \"id\": \"52\",\n                  \"name\": \"About Us\",\n                  \"type\": \"url\",\n                  \"url\": \"https://www.mozilla.org/en-US/about/\"\n               } ],\n               \"date_added\": \"13149362306510034\",\n               \"date_modified\": \"0\",\n               \"id\": \"48\",\n               \"name\": \"Mozilla Firefox\",\n               \"type\": \"folder\"\n            } ],\n            \"date_added\": \"13149362306507580\",\n            \"date_modified\": \"13149362306507581\",\n            \"id\": \"41\",\n            \"name\": \"Imported From Firefox (2011-09-02, 06:03:50)\",\n            \"type\": \"folder\"\n         } ],\n         \"date_added\": \"13149362288728239\",\n         \"date_modified\": \"0\",\n         \"id\": \"1\",\n         \"name\": \"Bookmarks bar\",\n         \"type\": \"folder\"\n      },\n      \"other\": {\n         \"children\": [ {\n            \"date_added\": \"13149416292911928\",\n            \"id\": \"55\",\n            \"name\": \"Google\",\n            \"type\": \"url\",\n            \"url\": \"https://www.google.com/\"\n         } ],\n         \"date_added\": \"13149362288728244\",\n         \"date_modified\": \"13149416292911928\",\n         \"id\": \"2\",\n         \"name\": \"Other bookmarks\",\n         \"type\": \"folder\"\n      },\n      \"synced\": {\n         \"children\": [  ],\n         \"date_added\": \"13149362288728244\",\n         \"date_modified\": \"0\",\n         \"id\": \"3\",\n         \"name\": \"Mobile bookmarks\",\n         \"type\": \"folder\"\n      }\n   },\n   \"version\": 1\n}\n"
  },
  {
    "path": "tests/test_bukuDb/firefox_res.yaml",
    "content": "? !!python/tuple ['http://voyagerlive.org/', Voyager, ',bookmarks toolbar,', null,\n  0, true, false]\n: {}\n? !!python/tuple ['http://wiki.ubuntu.com/', Ubuntu Wiki (community-edited website),\n  ',bookmarks menu,ubuntu  and free software links,', null, 0, true, false]\n: {}\n? !!python/tuple ['http://www.debian.org/', Debian (Ubuntu is based on Debian), ',bookmarks\n    menu,ubuntu  and free software links,', null, 0, true, false]\n: {}\n? !!python/tuple ['http://www.ubuntu.com/', Ubuntu, ',bookmarks menu,ubuntu  and free\n    software links,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://addons.mozilla.org/fr/firefox/addon/adblock-plus/', '',\n  ',language,tags,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://addons.mozilla.org/fr/firefox/addon/adblock-plus/', Adblock\n    Plus, ',bookmarks toolbar,f+,language,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://addons.mozilla.org/fr/firefox/addon/searchpreview/', SearchPreview,\n  ',bookmarks toolbar,f+,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://addons.mozilla.org/fr/firefox/language-tools/', '', ',hello,language,tags,',\n  null, 0, true, false]\n: {}\n? !!python/tuple ['https://addons.mozilla.org/fr/firefox/language-tools/', Language\n    Tools, ',bookmarks toolbar,f+,hello,language,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://answers.launchpad.net/ubuntu/+addquestion', Make a Support\n    Request to the Ubuntu Community, ',bookmarks menu,ubuntu  and free software links,',\n  null, 0, true, false]\n: {}\n? !!python/tuple ['https://one.ubuntu.com/', Ubuntu One - The personal cloud that\n    brings your digital life together, ',bookmarks menu,ubuntu  and free software links,',\n  null, 0, true, false]\n: {}\n? !!python/tuple ['https://www.google.co.in/', '', ',bookmarks menu,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://www.mozilla.org/en-US/about/', About Us, ',bookmarks menu,mozilla\n    firefox,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://www.mozilla.org/en-US/contribute/', Get Involved, ',bookmarks\n    menu,mozilla firefox,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://www.mozilla.org/en-US/firefox/customize/', Customize Firefox,\n  ',bookmarks menu,mozilla firefox,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://www.mozilla.org/en-US/firefox/help/', Help and Tutorials,\n  ',bookmarks menu,mozilla firefox,', null, 0, true, false]\n: {}\n"
  },
  {
    "path": "tests/test_bukuDb/firefox_res_nopt.yaml",
    "content": "? !!python/tuple ['http://voyagerlive.org/', Voyager, ',', null, 0, true, false]\n: {}\n? !!python/tuple ['http://wiki.ubuntu.com/', Ubuntu Wiki (community-edited website),\n  ',', null, 0, true, false]\n: {}\n? !!python/tuple ['http://www.debian.org/', Debian (Ubuntu is based on Debian), ',',\n  null, 0, true, false]\n: {}\n? !!python/tuple ['http://www.ubuntu.com/', Ubuntu, ',', null, 0, true, false]\n: {}\n? !!python/tuple ['https://addons.mozilla.org/fr/firefox/addon/adblock-plus/', '',\n  ',language,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://addons.mozilla.org/fr/firefox/addon/adblock-plus/', Adblock\n    Plus, ',language,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://addons.mozilla.org/fr/firefox/addon/searchpreview/', SearchPreview,\n  ',', null, 0, true, false]\n: {}\n? !!python/tuple ['https://addons.mozilla.org/fr/firefox/language-tools/', '', ',hello,language,',\n  null, 0, true, false]\n: {}\n? !!python/tuple ['https://addons.mozilla.org/fr/firefox/language-tools/', Language\n    Tools, ',hello,language,', null, 0, true, false]\n: {}\n? !!python/tuple ['https://answers.launchpad.net/ubuntu/+addquestion', Make a Support\n    Request to the Ubuntu Community, ',', null, 0, true, false]\n: {}\n? !!python/tuple ['https://one.ubuntu.com/', Ubuntu One - The personal cloud that\n    brings your digital life together, ',', null, 0, true, false]\n: {}\n? !!python/tuple ['https://www.google.co.in/', '', ',', null, 0, true, false]\n: {}\n? !!python/tuple ['https://www.mozilla.org/en-US/about/', About Us, ',', null, 0,\n  true, false]\n: {}\n? !!python/tuple ['https://www.mozilla.org/en-US/contribute/', Get Involved, ',',\n  null, 0, true, false]\n: {}\n? !!python/tuple ['https://www.mozilla.org/en-US/firefox/customize/', Customize Firefox,\n  ',', null, 0, true, false]\n: {}\n? !!python/tuple ['https://www.mozilla.org/en-US/firefox/help/', Help and Tutorials,\n  ',', null, 0, true, false]\n: {}\n"
  },
  {
    "path": "tests/test_bukuDb/places.sql",
    "content": "PRAGMA foreign_keys=OFF;\nBEGIN TRANSACTION;\nCREATE TABLE moz_places (   id INTEGER PRIMARY KEY, url LONGVARCHAR, title LONGVARCHAR, rev_host LONGVARCHAR, visit_count INTEGER DEFAULT 0, hidden INTEGER DEFAULT 0 NOT NULL, typed INTEGER DEFAULT 0 NOT NULL, favicon_id INTEGER, frecency INTEGER DEFAULT -1 NOT NULL, last_visit_date INTEGER , guid TEXT, foreign_count INTEGER DEFAULT 0 NOT NULL, url_hash INTEGER DEFAULT 0 NOT NULL);\nINSERT INTO moz_places VALUES(1,'place:folder=BOOKMARKS_MENU&folder=UNFILED_BOOKMARKS&folder=TOOLBAR&queryType=1&sort=12&maxResults=10&excludeQueries=1',NULL,NULL,0,1,0,NULL,0,NULL,'3L8RhCORmJhI',1,268506299617350);\nINSERT INTO moz_places VALUES(2,'place:type=6&sort=14&maxResults=10',NULL,NULL,0,1,0,NULL,0,NULL,'THKmZ1sqzg9e',1,268505606444332);\nINSERT INTO moz_places VALUES(3,'http://www.ubuntu.com/',NULL,'moc.utnubu.www.',0,0,0,NULL,64,NULL,'zZD-rGLsu3ta',1,125508050257634);\nINSERT INTO moz_places VALUES(4,'http://wiki.ubuntu.com/',NULL,'moc.utnubu.ikiw.',0,0,0,NULL,64,NULL,'buL63RhXNx5I',1,125511519733047);\nINSERT INTO moz_places VALUES(5,'https://answers.launchpad.net/ubuntu/+addquestion',NULL,'ten.daphcnual.srewsna.',0,0,0,NULL,64,NULL,'R7Y0VYN5Qdc0',1,47359338650210);\nINSERT INTO moz_places VALUES(6,'http://www.debian.org/',NULL,'gro.naibed.www.',0,0,0,NULL,64,NULL,'v3JUUM0EFXCG',1,125508165346216);\nINSERT INTO moz_places VALUES(7,'https://one.ubuntu.com/',NULL,'moc.utnubu.eno.',0,0,0,NULL,64,NULL,'AGUzUoXo3NUs',1,47359195550374);\nINSERT INTO moz_places VALUES(8,'https://www.mozilla.org/en-US/firefox/help/',NULL,'gro.allizom.www.',0,0,0,NULL,64,NULL,'j_Zj5HQyRB0C',1,47356364765622);\nINSERT INTO moz_places VALUES(9,'https://www.mozilla.org/en-US/firefox/customize/',NULL,'gro.allizom.www.',0,0,0,NULL,64,NULL,'geto35febbf0',1,47357014640010);\nINSERT INTO moz_places VALUES(10,'https://www.mozilla.org/en-US/contribute/',NULL,'gro.allizom.www.',0,0,0,NULL,64,NULL,'Rk5TBl9nW_0G',1,47358034485371);\nINSERT INTO moz_places VALUES(11,'https://www.mozilla.org/en-US/about/',NULL,'gro.allizom.www.',0,0,0,NULL,64,NULL,'ajjJNGoeVZos',1,47358774953055);\nINSERT INTO moz_places VALUES(12,'place:sort=8&maxResults=10',NULL,NULL,0,1,0,NULL,0,NULL,'vDKpX_SQJZNF',1,268505095842199);\nINSERT INTO moz_places VALUES(13,'http://voyagerlive.org/',NULL,'gro.evilregayov.',0,0,0,NULL,64,NULL,'Tg0y_kuW7hAd',1,125507606885498);\nINSERT INTO moz_places VALUES(14,'file:///.startpage/0/index.html',NULL,'.',7,0,0,NULL,513,1468499310694226,'k3q8NHlxwJ8d',1,219669470494363);\nINSERT INTO moz_places VALUES(15,'file:///.startpage/1/index.html',NULL,'.',0,0,0,NULL,64,NULL,'4VbS_4dqJ79q',1,219668736475133);\nINSERT INTO moz_places VALUES(16,'file:///.startpage/2/index.html',NULL,'.',0,0,0,NULL,64,NULL,'D8KMVz0PnhzZ',1,219669324140854);\nINSERT INTO moz_places VALUES(17,'file:///.startpage/3/index.html',NULL,'.',0,0,0,NULL,64,NULL,'8ybRhb7Km8vY',1,219667182856922);\nINSERT INTO moz_places VALUES(18,'file:///.startpage/4/index.html',NULL,'.',0,0,0,NULL,64,NULL,'N5CP9cHa9ZDM',1,219669164690790);\nINSERT INTO moz_places VALUES(19,'file:///.startpage/5/index.html',NULL,'.',0,0,0,NULL,64,NULL,'4GND-j6aizWA',1,219669839582445);\nINSERT INTO moz_places VALUES(20,'file:///.startpage/6/index.html',NULL,'.',0,0,0,NULL,64,NULL,'AlCg03lD3iJL',1,219667108107103);\nINSERT INTO moz_places VALUES(21,'file:///.startpage/7/index.html',NULL,'.',0,0,0,NULL,64,NULL,'OTvU7l-YdvmN',1,219668183071102);\nINSERT INTO moz_places VALUES(22,'file:///.startpage/8/index.html',NULL,'.',0,0,0,NULL,64,NULL,'IPI7YoUqVbqd',1,219668476324512);\nINSERT INTO moz_places VALUES(23,'file:///.startpage/9/index.html',NULL,'.',0,0,0,NULL,64,NULL,'5kmSOb_Xe4I_',1,219669049854574);\nINSERT INTO moz_places VALUES(24,'file:///.startpage/10/index.html',NULL,'.',0,0,0,NULL,64,NULL,'BoSf-LyJzWzu',1,219669428108279);\nINSERT INTO moz_places VALUES(25,'file:///.startpage/11/index.html',NULL,'.',0,0,0,NULL,64,NULL,'216qE1FWvaJN',1,219668510215844);\nINSERT INTO moz_places VALUES(26,'file:///.startpage/12/index.html',NULL,'.',0,0,0,NULL,64,NULL,'hVpHyr-MGelW',1,219666403675874);\nINSERT INTO moz_places VALUES(27,'file:///.startpage/13/index.html',NULL,'.',0,0,0,NULL,64,NULL,'2r8eRn19hmv4',1,219667091819632);\nINSERT INTO moz_places VALUES(28,'file:///.startpage/14/index.html',NULL,'.',0,0,0,NULL,64,NULL,'vxAWivZ0jJmP',1,219668747999666);\nINSERT INTO moz_places VALUES(29,'file:///.startpage/15/index.html',NULL,'.',0,0,0,NULL,64,NULL,'_80kLPC1SXsf',1,219668718232164);\nINSERT INTO moz_places VALUES(30,'file:///.startpage/16/index.html',NULL,'.',0,0,0,NULL,64,NULL,'RQTXoE_MEOin',1,219668369570932);\nINSERT INTO moz_places VALUES(31,'file:///.startpage/17/index.html',NULL,'.',0,0,0,NULL,64,NULL,'4hvYmF1jW_OE',1,219666243124822);\nINSERT INTO moz_places VALUES(32,'file:///.startpage/18/index.html',NULL,'.',0,0,0,NULL,64,NULL,'zPKBW3WnmK0M',1,219670060396334);\nINSERT INTO moz_places VALUES(33,'file:///.startpage/19/index.html',NULL,'.',0,0,0,NULL,64,NULL,'AvLKOMNhR38s',1,219669203074249);\nINSERT INTO moz_places VALUES(34,'file:///.startpage/20/index.html',NULL,'.',0,0,0,NULL,64,NULL,'CFEXVi6ajxMJ',1,219667678613885);\nINSERT INTO moz_places VALUES(35,'file:///.startpage/21/index.html',NULL,'.',0,0,0,NULL,64,NULL,'9zywynfkqAEh',1,219667658155164);\nINSERT INTO moz_places VALUES(36,'file:///.startpage/22/index.html',NULL,'.',0,0,0,NULL,64,NULL,'n12ZqgkI0xgv',1,219669359046484);\nINSERT INTO moz_places VALUES(37,'file:///.startpage/23/index.html',NULL,'.',0,0,0,NULL,64,NULL,'dV1nUAJuxYhJ',1,219667663302713);\nINSERT INTO moz_places VALUES(38,'file:///.startpage/24/index.html',NULL,'.',0,0,0,NULL,64,NULL,'bHFeAKm2VoxV',1,219668865743796);\nINSERT INTO moz_places VALUES(39,'file:///.startpage/25/index.html',NULL,'.',0,0,0,NULL,64,NULL,'ZlveTgWYOHyl',1,219666516621298);\nINSERT INTO moz_places VALUES(40,'file:///.startpage/26/index.html',NULL,'.',0,0,0,NULL,64,NULL,'LT0TSp-aRcGW',1,219669783839253);\nINSERT INTO moz_places VALUES(41,'file:///.startpage/27/index.html',NULL,'.',0,0,0,NULL,64,NULL,'3L5m6XPVjUwh',1,219667796938995);\nINSERT INTO moz_places VALUES(42,'apt://flashplugin-installer',NULL,NULL,0,0,0,NULL,64,NULL,'wk7Q00E1AJtJ',1,130688315308473);\nINSERT INTO moz_places VALUES(43,'https://addons.mozilla.org/fr/firefox/addon/adblock-plus/',NULL,'gro.allizom.snodda.',0,0,0,NULL,64,NULL,'BQTNVCrOi6cy',2,47359114341489);\nINSERT INTO moz_places VALUES(44,'https://addons.mozilla.org/fr/firefox/addon/searchpreview/','SearchPreview :: Modules pour Firefox','gro.allizom.snodda.',1,0,0,32,120,1500352310710639,'ISDOqrYxP-o2',1,47359086811128);\nINSERT INTO moz_places VALUES(45,'https://addons.mozilla.org/fr/firefox/language-tools/','Dictionnaires et paquetages linguistiques :: Modules pour Firefox','gro.allizom.snodda.',1,0,0,32,135,1502469203205795,'ejTA_lnpIVGp',3,47356529039849);\nINSERT INTO moz_places VALUES(86,'place:type=3&sort=4',NULL,NULL,0,1,0,NULL,0,NULL,'0P7W4IkZ9d-x',1,268506321168863);\nINSERT INTO moz_places VALUES(87,'place:transition=7&sort=4',NULL,NULL,0,1,0,NULL,0,NULL,'pfR1SwF8WV5b',1,268507063987631);\nINSERT INTO moz_places VALUES(88,'place:type=6&sort=1',NULL,NULL,0,1,0,NULL,0,NULL,'4bHN8NzrU69O',1,268504878649678);\nINSERT INTO moz_places VALUES(89,'place:folder=TOOLBAR',NULL,NULL,0,1,0,NULL,0,NULL,'-UNrGlY2qHS-',1,268507032739381);\nINSERT INTO moz_places VALUES(90,'place:folder=BOOKMARKS_MENU',NULL,NULL,0,1,0,NULL,0,NULL,'_ZtxELNAGorr',1,268504983346218);\nINSERT INTO moz_places VALUES(91,'place:folder=UNFILED_BOOKMARKS',NULL,NULL,0,1,0,NULL,0,NULL,'28nTBc-Glbdq',1,268507911875319);\nINSERT INTO moz_places VALUES(1896,'https://www.google.co.in/','Google','ni.oc.elgoog.www.',2,0,1,11,2698,1504975524486254,'j_p-Y6RhkHko',1,47357009725544);\nCREATE TABLE moz_historyvisits (  id INTEGER PRIMARY KEY, from_visit INTEGER, place_id INTEGER, visit_date INTEGER, visit_type INTEGER, session INTEGER);\nCREATE TABLE moz_inputhistory (  place_id INTEGER NOT NULL, input LONGVARCHAR NOT NULL, use_count INTEGER, PRIMARY KEY (place_id, input));\nCREATE TABLE moz_hosts (  id INTEGER PRIMARY KEY, host TEXT NOT NULL UNIQUE, frecency INTEGER, typed INTEGER NOT NULL DEFAULT 0, prefix TEXT);\nCREATE TABLE moz_bookmarks (  id INTEGER PRIMARY KEY, type INTEGER, fk INTEGER DEFAULT NULL, parent INTEGER, position INTEGER, title LONGVARCHAR, keyword_id INTEGER, folder_type TEXT, dateAdded INTEGER, lastModified INTEGER, guid TEXT, syncStatus INTEGER DEFAULT 0 NOT NULL, syncChangeCounter INTEGER DEFAULT 1 NOT NULL);\nINSERT INTO moz_bookmarks VALUES(1,2,NULL,0,0,'',NULL,NULL,1467725517327000,1470428315627000,'root________',0,1);\nINSERT INTO moz_bookmarks VALUES(2,2,NULL,1,0,'Bookmarks Menu',NULL,NULL,1467725517327000,1504975536882000,'menu________',0,4);\nINSERT INTO moz_bookmarks VALUES(3,2,NULL,1,1,'Bookmarks Toolbar',NULL,NULL,1467725517327000,1470428315492000,'toolbar_____',0,1);\nINSERT INTO moz_bookmarks VALUES(4,2,NULL,1,2,'Tags',NULL,NULL,1467725517327000,1502516392069000,'tags________',0,128);\nINSERT INTO moz_bookmarks VALUES(6,1,1,2,0,'Marqués récemment',NULL,NULL,1440206436707000,1470428315238000,'ePlbveDPxcVC',0,1);\nINSERT INTO moz_bookmarks VALUES(9,2,NULL,2,3,'Ubuntu, and Free Software links',NULL,NULL,1440206436614000,1467725517796000,'IIOoE1AVQALy',0,1);\nINSERT INTO moz_bookmarks VALUES(10,1,3,9,0,'Ubuntu',NULL,NULL,1440206436615000,1440206436616000,'BdTJgxwzX-bY',0,1);\nINSERT INTO moz_bookmarks VALUES(11,1,4,9,1,'Ubuntu Wiki (community-edited website)',NULL,NULL,1440206436617000,1440206436618000,'Q3CHtiX8BAQm',0,1);\nINSERT INTO moz_bookmarks VALUES(12,1,5,9,2,'Make a Support Request to the Ubuntu Community',NULL,NULL,1440206436618000,1440206436619000,'abuWmpFRfxHw',0,1);\nINSERT INTO moz_bookmarks VALUES(13,1,6,9,3,'Debian (Ubuntu is based on Debian)',NULL,NULL,1440206436619000,1440206436621000,'09Wm3713Bb1z',0,1);\nINSERT INTO moz_bookmarks VALUES(14,1,7,9,4,'Ubuntu One - The personal cloud that brings your digital life together',NULL,NULL,1440206436622000,1440206436622000,'BkV2PLxgT2hL',0,1);\nINSERT INTO moz_bookmarks VALUES(15,2,NULL,2,4,'Mozilla Firefox',NULL,NULL,1440206436622000,1440206436627000,'4TFNf2K_7e1a',0,1);\nINSERT INTO moz_bookmarks VALUES(16,1,8,15,0,'Help and Tutorials',NULL,NULL,1440206436623000,1440206436624000,'vC7MQXkMREQV',0,1);\nINSERT INTO moz_bookmarks VALUES(17,1,9,15,1,'Customize Firefox',NULL,NULL,1440206436624000,1440206436625000,'hNcuVhGLSBMT',0,1);\nINSERT INTO moz_bookmarks VALUES(18,1,10,15,2,'Get Involved',NULL,NULL,1440206436625000,1440206436626000,'ChmDGJ8Nh2Rj',0,1);\nINSERT INTO moz_bookmarks VALUES(19,1,11,15,3,'About Us',NULL,NULL,1440206436627000,1440206436627000,'4KMQLdKoA5ME',0,1);\nINSERT INTO moz_bookmarks VALUES(21,1,13,3,1,'Voyager',NULL,NULL,1440206499903000,1440206519634000,'GOB7TZA0gCGU',0,1);\nINSERT INTO moz_bookmarks VALUES(23,1,14,22,0,'0',NULL,NULL,1440206567367000,1440206589837000,'e_UDXJIzf2zR',0,1);\nINSERT INTO moz_bookmarks VALUES(24,1,15,22,1,'1',NULL,NULL,1440206671321000,1440206683533000,'2hZfPJd4TtJn',0,1);\nINSERT INTO moz_bookmarks VALUES(25,1,16,22,2,'2',NULL,NULL,1440206671321000,1440206770420000,'W2p3TBdOtDvH',0,1);\nINSERT INTO moz_bookmarks VALUES(26,1,17,22,3,'3',NULL,NULL,1440206671321000,1440206784296000,'qZYep98FnR__',0,1);\nINSERT INTO moz_bookmarks VALUES(27,1,18,22,4,'4',NULL,NULL,1440206671321000,1440206793116000,'zZu4UGpXYow3',0,1);\nINSERT INTO moz_bookmarks VALUES(28,1,19,22,5,'5',NULL,NULL,1440206671321000,1440206801937000,'VXZ3mBzzcA4Q',0,1);\nINSERT INTO moz_bookmarks VALUES(29,1,20,22,6,'6',NULL,NULL,1440206671321000,1440206810195000,'a0RFsnB3EwVc',0,1);\nINSERT INTO moz_bookmarks VALUES(30,1,21,22,7,'7',NULL,NULL,1440206671321000,1440206817630000,'6Biu707lOdHg',0,1);\nINSERT INTO moz_bookmarks VALUES(31,1,22,22,8,'8',NULL,NULL,1440206671321000,1440206825212000,'AP1mXZa0ahQ4',0,1);\nINSERT INTO moz_bookmarks VALUES(32,1,23,22,9,'9',NULL,NULL,1440206671321000,1440206832966000,'WxjKjeWFlrMP',0,1);\nINSERT INTO moz_bookmarks VALUES(33,1,24,22,10,'10',NULL,NULL,1440206671321000,1440206844808000,'-QeCAla6-KY8',0,1);\nINSERT INTO moz_bookmarks VALUES(34,1,25,22,11,'11',NULL,NULL,1440206671321000,1440206849594000,'ZN_9hCgM4lf0',0,1);\nINSERT INTO moz_bookmarks VALUES(35,1,26,22,12,'12',NULL,NULL,1440206671321000,1440206856076000,'oWFTNWyZ6gMq',0,1);\nINSERT INTO moz_bookmarks VALUES(36,1,27,22,13,'13',NULL,NULL,1440206671321000,1440206867754000,'-lcyQ56ww8NA',0,1);\nINSERT INTO moz_bookmarks VALUES(37,1,28,22,14,'14',NULL,NULL,1440206671321000,1440206910367000,'w43SFrygDRwR',0,1);\nINSERT INTO moz_bookmarks VALUES(38,1,29,22,15,'15',NULL,NULL,1440206671321000,1440206917428000,'8Q-1muzTEeWE',0,1);\nINSERT INTO moz_bookmarks VALUES(39,1,30,22,16,'16',NULL,NULL,1440206671321000,1440206928910000,'Ghx1rkNDlp4H',0,1);\nINSERT INTO moz_bookmarks VALUES(40,1,31,22,17,'17',NULL,NULL,1440206671321000,1440206935983000,'d2AJXtLXpkFb',0,1);\nINSERT INTO moz_bookmarks VALUES(41,1,32,22,18,'18',NULL,NULL,1440206671321000,1440206942585000,'MMo9b33f9xyE',0,1);\nINSERT INTO moz_bookmarks VALUES(42,1,33,22,19,'19',NULL,NULL,1440206671321000,1440206950138000,'K7socCLnYy4L',0,1);\nINSERT INTO moz_bookmarks VALUES(43,1,34,22,20,'20',NULL,NULL,1440206671321000,1440206957207000,'5V11irxjjd38',0,1);\nINSERT INTO moz_bookmarks VALUES(44,1,35,22,21,'21',NULL,NULL,1440206671321000,1440206964605000,'-oxnXIHTe0Qa',0,1);\nINSERT INTO moz_bookmarks VALUES(45,1,36,22,22,'22',NULL,NULL,1440206671321000,1440206971117000,'EKZ1Rmek4lTO',0,1);\nINSERT INTO moz_bookmarks VALUES(46,1,37,22,23,'23',NULL,NULL,1440206671321000,1440206978351000,'gpzcG4LjZ2Re',0,1);\nINSERT INTO moz_bookmarks VALUES(47,1,38,22,24,'24',NULL,NULL,1440206671321000,1440737505375000,'PvDZnqwbwj4u',0,1);\nINSERT INTO moz_bookmarks VALUES(48,1,39,22,25,'25',NULL,NULL,1440206671321000,1440737513706000,'BuKY3E03NnmF',0,1);\nINSERT INTO moz_bookmarks VALUES(49,1,40,22,26,'26',NULL,NULL,1440206671321000,1440737524663000,'lf6NCbNyKMnX',0,1);\nINSERT INTO moz_bookmarks VALUES(50,1,41,22,27,'27',NULL,NULL,1440206671321000,1440737540732000,'StBEBdR-nxjO',0,1);\nINSERT INTO moz_bookmarks VALUES(51,2,NULL,3,3,'F+',NULL,NULL,1440207026112000,1440207159607000,'njpFXZMf3dGr',0,1);\nINSERT INTO moz_bookmarks VALUES(52,1,42,51,0,'Flash Install',NULL,NULL,1440207037033000,1440207058958000,'FSJNRAVVHZw2',0,1);\nINSERT INTO moz_bookmarks VALUES(53,1,43,51,1,'Adblock Plus',NULL,NULL,1440207069038000,1440207082178000,'IrUa9S-JhU_c',0,1);\nINSERT INTO moz_bookmarks VALUES(54,1,44,51,2,'SearchPreview',NULL,NULL,1440207088169000,1500352294532000,'yS24nxSktibH',0,2);\nINSERT INTO moz_bookmarks VALUES(55,1,45,51,3,'Language Tools',NULL,NULL,1440207113748000,1440207156708000,'Wp2tuVDbc5tZ',0,2);\nINSERT INTO moz_bookmarks VALUES(57,1,86,56,0,'History',NULL,NULL,1468512203179000,1468512203180000,'XNGjCIZLlYX7',0,1);\nINSERT INTO moz_bookmarks VALUES(58,1,87,56,1,'Downloads',NULL,NULL,1468512203180000,1468512203181000,'C4MPThOkxXJp',0,1);\nINSERT INTO moz_bookmarks VALUES(59,1,88,56,2,'Tags',NULL,NULL,1468512203181000,1468512203182000,'gpxshKA-aBjG',0,1);\nINSERT INTO moz_bookmarks VALUES(61,1,89,60,0,NULL,NULL,NULL,1468512203184000,1468512203185000,'NrSbqdjDsUqF',0,1);\nINSERT INTO moz_bookmarks VALUES(62,1,90,60,1,NULL,NULL,NULL,1468512203185000,1468512203186000,'BaYiLZ99gJSr',0,1);\nINSERT INTO moz_bookmarks VALUES(63,1,91,60,2,NULL,NULL,NULL,1468512203186000,1468512203187000,'w-U2zBo1eOHU',0,1);\nINSERT INTO moz_bookmarks VALUES(64,1,12,3,0,'Most Visited',NULL,NULL,1470428315492000,1470428315588000,'RtkxwHiU0MDn',0,1);\nINSERT INTO moz_bookmarks VALUES(65,1,2,2,1,'Recent Tags',NULL,NULL,1470428315627000,1470428315690000,'fLDmWrAmGNWW',0,1);\nINSERT INTO moz_bookmarks VALUES(67,2,NULL,4,0,'language',NULL,NULL,1491534915350000,1491534938078000,'4bL9ZoFmnBLt',0,1);\nINSERT INTO moz_bookmarks VALUES(68,1,45,67,0,NULL,NULL,NULL,1491534915363000,1491534915363000,'49l3q4-FiBp0',0,2);\nINSERT INTO moz_bookmarks VALUES(69,1,43,67,1,NULL,NULL,NULL,1491534938078000,1491534938078000,'d8z-e3rN3r4T',0,1);\nINSERT INTO moz_bookmarks VALUES(70,2,NULL,4,1,'hello',NULL,NULL,1502469210372000,1502469210376000,'TpKnUAtkhxK6',1,2);\nINSERT INTO moz_bookmarks VALUES(71,1,45,70,0,NULL,NULL,NULL,1502469210376000,1502469210376000,'2Qo36cSCjizq',1,2);\nINSERT INTO moz_bookmarks VALUES(72,1,1896,2,5,'',NULL,NULL,1504975536882000,1504976368646000,'uWFrltEh9OTA',1,4);\nCREATE TABLE moz_keywords (  id INTEGER PRIMARY KEY AUTOINCREMENT, keyword TEXT UNIQUE, place_id INTEGER, post_data TEXT);\nCREATE TABLE moz_favicons (  id INTEGER PRIMARY KEY, url LONGVARCHAR UNIQUE, data BLOB, mime_type VARCHAR(32), expiration LONG);\nCREATE TABLE moz_anno_attributes (  id INTEGER PRIMARY KEY, name VARCHAR(32) UNIQUE NOT NULL);\nCREATE TABLE moz_annos (  id INTEGER PRIMARY KEY, place_id INTEGER NOT NULL, anno_attribute_id INTEGER, mime_type VARCHAR(32) DEFAULT NULL, content LONGVARCHAR, flags INTEGER DEFAULT 0, expiration INTEGER DEFAULT 0, type INTEGER DEFAULT 0, dateAdded INTEGER DEFAULT 0, lastModified INTEGER DEFAULT 0);\nCREATE TABLE moz_items_annos (  id INTEGER PRIMARY KEY, item_id INTEGER NOT NULL, anno_attribute_id INTEGER, mime_type VARCHAR(32) DEFAULT NULL, content LONGVARCHAR, flags INTEGER DEFAULT 0, expiration INTEGER DEFAULT 0, type INTEGER DEFAULT 0, dateAdded INTEGER DEFAULT 0, lastModified INTEGER DEFAULT 0);\nANALYZE sqlite_schema;\nINSERT INTO sqlite_stat1 VALUES('moz_places','moz_places_guid_uniqueindex','1535 1');\nINSERT INTO sqlite_stat1 VALUES('moz_places','moz_places_url_hashindex','1535 1');\nINSERT INTO sqlite_stat1 VALUES('moz_places','moz_places_lastvisitdateindex','1535 2');\nINSERT INTO sqlite_stat1 VALUES('moz_places','moz_places_frecencyindex','1535 9');\nINSERT INTO sqlite_stat1 VALUES('moz_places','moz_places_visitcount','1535 86');\nINSERT INTO sqlite_stat1 VALUES('moz_places','moz_places_hostindex','1535 8');\nINSERT INTO sqlite_stat1 VALUES('moz_places','moz_places_faviconindex','1535 12');\nINSERT INTO sqlite_stat1 VALUES('moz_bookmarks','moz_bookmarks_guid_uniqueindex','69 1');\nINSERT INTO sqlite_stat1 VALUES('moz_bookmarks','moz_bookmarks_itemlastmodifiedindex','69 2 1');\nINSERT INTO sqlite_stat1 VALUES('moz_bookmarks','moz_bookmarks_parentindex','69 6 1');\nINSERT INTO sqlite_stat1 VALUES('moz_bookmarks','moz_bookmarks_itemindex','69 2 2');\nINSERT INTO sqlite_stat1 VALUES('moz_historyvisits','moz_historyvisits_dateindex','2151 1');\nINSERT INTO sqlite_stat1 VALUES('moz_historyvisits','moz_historyvisits_fromindex','2151 3');\nINSERT INTO sqlite_stat1 VALUES('moz_historyvisits','moz_historyvisits_placedateindex','2151 2 1');\nINSERT INTO sqlite_stat1 VALUES('moz_inputhistory','sqlite_autoindex_moz_inputhistory_1','36 2 1');\nCREATE TABLE moz_bookmarks_deleted (  guid TEXT PRIMARY KEY, dateRemoved INTEGER NOT NULL DEFAULT 0);\nDELETE FROM sqlite_sequence;\nCREATE INDEX moz_places_faviconindex ON moz_places (favicon_id);\nCREATE INDEX moz_places_hostindex ON moz_places (rev_host);\nCREATE INDEX moz_places_visitcount ON moz_places (visit_count);\nCREATE INDEX moz_places_frecencyindex ON moz_places (frecency);\nCREATE INDEX moz_places_lastvisitdateindex ON moz_places (last_visit_date);\nCREATE INDEX moz_historyvisits_placedateindex ON moz_historyvisits (place_id, visit_date);\nCREATE INDEX moz_historyvisits_fromindex ON moz_historyvisits (from_visit);\nCREATE INDEX moz_historyvisits_dateindex ON moz_historyvisits (visit_date);\nCREATE INDEX moz_bookmarks_itemindex ON moz_bookmarks (fk, type);\nCREATE INDEX moz_bookmarks_parentindex ON moz_bookmarks (parent, position);\nCREATE INDEX moz_bookmarks_itemlastmodifiedindex ON moz_bookmarks (fk, lastModified);\nCREATE INDEX moz_places_url_hashindex ON moz_places (url_hash);\nCREATE UNIQUE INDEX moz_places_guid_uniqueindex ON moz_places (guid);\nCREATE UNIQUE INDEX moz_bookmarks_guid_uniqueindex ON moz_bookmarks (guid);\nCREATE UNIQUE INDEX moz_keywords_placepostdata_uniqueindex ON moz_keywords (place_id, post_data);\nCREATE UNIQUE INDEX moz_annos_placeattributeindex ON moz_annos (place_id, anno_attribute_id);\nCREATE UNIQUE INDEX moz_items_annos_itemattributeindex ON moz_items_annos (item_id, anno_attribute_id);\nCOMMIT;\n"
  },
  {
    "path": "tests/test_bukuDb.py",
    "content": "#!/usr/bin/env python3\n#\n# Unit test cases for buku\n#\nimport math\nimport os\nimport re\nimport sqlite3\nimport sys\nimport unittest\nfrom genericpath import exists\nfrom tempfile import NamedTemporaryFile, TemporaryDirectory\nfrom random import shuffle\nfrom unittest import mock\n\nimport pytest\nimport yaml\nfrom hypothesis import example, given, settings\nfrom hypothesis import strategies as st\n\nfrom buku import PERMANENT_REDIRECTS, BukuDb, FetchResult, BookmarkVar, bookmark_vars, parse_tags, prompt\nfrom tests.util import mock_fetch, _add_rec, _tagset\n\n\ndef get_temp_dir_path():\n    with TemporaryDirectory(prefix=\"bukutest_\") as dir_obj:\n        return dir_obj\n\nTEST_TEMP_DIR_PATH = get_temp_dir_path()\nTEST_TEMP_DBDIR_PATH = os.path.join(TEST_TEMP_DIR_PATH, \"buku\")\nTEST_TEMP_DBFILE_PATH = os.path.join(TEST_TEMP_DBDIR_PATH, \"bookmarks.db\")\nMAX_SQLITE_INT = int(math.pow(2, 63) - 1)\nTEST_PRINT_REC = (\"https://example.com\", \"\", parse_tags([\"cat,ant,bee,1\"]), \"\")\n\nTEST_BOOKMARKS = [\n    [\n        \"http://slashdot.org\",\n        \"SLASHDOT\",\n        parse_tags([\"old,news\"]),\n        \"News for old nerds, stuff that doesn't matter\",\n    ],\n    [\n        \"http://www.zażółćgęśląjaźń.pl/\",\n        \"ZAŻÓŁĆ\",\n        parse_tags([\"zażółć,gęślą,jaźń\"]),\n        \"Testing UTF-8, zażółć gęślą jaźń.\",\n    ],\n    [\n        \"http://example.com/\",\n        \"test\",\n        parse_tags([\"test,tes,est,es\"]),\n        \"a case for replace_tag test\",\n    ],\n]\n\nonly_python_3_5 = pytest.mark.skipif(\n    sys.version_info < (3, 5), reason=\"requires Python 3.5 or later\"\n)\n\n\n@pytest.fixture(scope=\"module\")\ndef vcr_cassette_dir(request):\n    # Put all cassettes in vhs/{module}/{test}.yaml\n    return os.path.join(\"tests\", \"vcr_cassettes\", request.module.__name__)\n\n\ndef rmdb(*bdbs):\n    for bdb in bdbs:\n        bdb.close()\n    if exists(TEST_TEMP_DBFILE_PATH):\n        os.remove(TEST_TEMP_DBFILE_PATH)\n\n\n@pytest.fixture()\ndef bukuDb():\n    os.environ[\"XDG_DATA_HOME\"] = TEST_TEMP_DIR_PATH\n\n    # start every test from a clean state\n    rmdb()\n\n    bdbs = []\n\n    def _bukuDb(*args, **kwargs):\n        nonlocal bdbs\n        bdbs += [BukuDb(*args, **kwargs)]\n        return bdbs[-1]\n\n    yield _bukuDb\n    rmdb(*bdbs)\n\n\nclass PrettySafeLoader(\n    yaml.SafeLoader\n):  # pylint: disable=too-many-ancestors,too-few-public-methods\n    def construct_python_tuple(self, node):\n        return tuple(self.construct_sequence(node))\n\n\nPrettySafeLoader.add_constructor(\n    \"tag:yaml.org,2002:python/tuple\", PrettySafeLoader.construct_python_tuple\n)\n\n\nclass TestBukuDb(unittest.TestCase):\n    def setUp(self):\n        os.environ[\"XDG_DATA_HOME\"] = TEST_TEMP_DIR_PATH\n\n        # start every test from a clean state\n        rmdb()\n\n        self.bookmarks = TEST_BOOKMARKS\n        self.bdb = BukuDb()\n\n    def tearDown(self):\n        os.environ[\"XDG_DATA_HOME\"] = TEST_TEMP_DIR_PATH\n        rmdb(self.bdb)\n\n    @pytest.mark.non_tox\n    def test_get_default_dbdir(self):\n        dbdir_expected = TEST_TEMP_DBDIR_PATH\n        home = os.path.expanduser(\"~\")\n        dbdir_local_expected = (os.path.join(home, \".local\", \"share\", \"buku\") if sys.platform != 'win32' else\n                                os.path.join(home, \"AppData\", \"Roaming\", \"buku\"))\n        dbdir_relative_expected = os.path.abspath(\".\")\n\n        # desktop linux\n        self.assertEqual(dbdir_expected, BukuDb.get_default_dbdir())\n\n        # desktop generic\n        os.environ.pop(\"XDG_DATA_HOME\")\n        self.assertEqual(dbdir_local_expected, BukuDb.get_default_dbdir())\n\n        # no desktop\n\n        # -- home is defined differently on various platforms.\n        # -- keep a copy and set it back once done\n        originals = {}\n        for env_var in [\"HOME\", \"HOMEPATH\", \"HOMEDIR\", \"APPDATA\"]:\n            if env_var in os.environ:\n                originals[env_var] = os.environ.pop(env_var)\n        try:\n            self.assertEqual(dbdir_relative_expected, BukuDb.get_default_dbdir())\n        finally:\n            os.environ.update(originals)\n\n    # # not sure how to test this in nondestructive manner\n    # def test_move_legacy_dbfile(self):\n    #     self.fail()\n\n    def test_initdb(self):\n        rmdb(self.bdb)\n        self.assertIs(False, exists(TEST_TEMP_DBFILE_PATH))\n        try:\n            conn, curr = BukuDb.initdb()\n            self.assertIsInstance(conn, sqlite3.Connection)\n            self.assertIsInstance(curr, sqlite3.Cursor)\n            self.assertIs(True, exists(TEST_TEMP_DBFILE_PATH))\n        finally:\n            curr.close()\n            conn.close()\n\n    def test_get_rec_by_id(self):\n        for bookmark in self.bookmarks:\n            # adding bookmark from self.bookmarks\n            _add_rec(self.bdb, *bookmark)\n\n        # the expected bookmark\n        expected = (1,) + tuple(TEST_BOOKMARKS[0]) + (0,)\n        bookmark_from_db = self.bdb.get_rec_by_id(1)\n        # asserting bookmark matches expected\n        self.assertEqual(expected, bookmark_from_db)\n        # asserting None returned if index out of range\n        self.assertIsNone(self.bdb.get_rec_by_id(len(self.bookmarks[0]) + 1))\n\n    def test_get_rec_all_by_ids(self):\n        for bookmark in self.bookmarks:\n            # adding bookmark from self.bookmarks\n            _add_rec(self.bdb, *bookmark)\n        expected = [(i+1,) + tuple(TEST_BOOKMARKS[i]) + (0,) for i in [0, 2]]\n        bookmarks_from_db = self.bdb.get_rec_all_by_ids([3, 1, 1, 3, 5])  # ignoring order and duplicates\n        self.assertEqual(expected, bookmarks_from_db)\n\n    def test_get_rec_id(self):\n        for idx, bookmark in enumerate(self.bookmarks):\n            # adding bookmark from self.bookmarks to database\n            _add_rec(self.bdb, *bookmark)\n            # asserting index is in order\n            idx_from_db = self.bdb.get_rec_id(bookmark[0])\n            self.assertEqual(idx + 1, idx_from_db)\n\n        # asserting None is returned for nonexistent url\n        idx_from_db = self.bdb.get_rec_id(\"http://nonexistent.url\")\n        self.assertIsNone(idx_from_db)\n\n    def test_add_rec(self):\n        for bookmark in self.bookmarks:\n            # adding bookmark from self.bookmarks to database\n            self.bdb.add_rec(*bookmark, fetch=False)\n            # retrieving bookmark from database\n            index = self.bdb.get_rec_id(bookmark[0])\n            from_db = self.bdb.get_rec_by_id(index)\n            self.assertIsNotNone(from_db)\n            # comparing data\n            for pair in zip(from_db[1:], bookmark):\n                self.assertEqual(*pair)\n\n    def test_swap_recs(self):\n        for bookmark in self.bookmarks:\n            _add_rec(self.bdb, *bookmark)\n        for id1, id2 in [(0, 1), (1, 4), (1, 1)]:\n            self.assertFalse(self.bdb.swap_recs(id1, id2), 'Not a valid index pair: (%d, %d)' % (id1, id2))\n        self.assertTrue(self.bdb.swap_recs(1, 3), 'This one should be valid')  # 3, 2, 1\n        self.assertEqual([x[0] for x in reversed(self.bookmarks)], [x.url for x in self.bdb.get_rec_all()])\n\n    # TODO: tags should be passed to the api as a sequence...\n    def test_suggest_tags(self):\n        for bookmark in self.bookmarks:\n            _add_rec(self.bdb, *bookmark)\n\n        tagstr = \",test,old,\"\n        with mock.patch(\"builtins.input\", return_value=\"1 2 3\"):\n            expected_results = \",es,est,news,old,test,\"\n            suggested_results = self.bdb.suggest_similar_tag(tagstr)\n            self.assertEqual(expected_results, suggested_results)\n\n        # returns user supplied tags if none are in the DB\n        tagstr = \",uniquetag1,uniquetag2,\"\n        expected_results = tagstr\n        suggested_results = self.bdb.suggest_similar_tag(tagstr)\n        self.assertEqual(expected_results, suggested_results)\n\n    def test_update_rec(self):\n        old_values = self.bookmarks[0]\n        new_values = self.bookmarks[1]\n\n        # adding bookmark and getting index\n        _add_rec(self.bdb, *old_values)\n        index = self.bdb.get_rec_id(old_values[0])\n        # updating with new values\n        self.bdb.update_rec(index, *new_values)\n        # retrieving bookmark from database\n        from_db = self.bdb.get_rec_by_id(index)\n        self.assertIsNotNone(from_db)\n        # checking if values are updated\n        for pair in zip(from_db[1:], new_values):\n            self.assertEqual(*pair)\n\n    def test_append_tag_at_index(self):\n        for bookmark in self.bookmarks:\n            _add_rec(self.bdb, *bookmark)\n\n        # tags to add\n        old_tags = self.bdb.get_rec_by_id(1)[3]\n        new_tags = \",foo,bar,baz\"\n        self.bdb.append_tag_at_index(1, new_tags)\n        # updated list of tags\n        from_db = self.bdb.get_rec_by_id(1)[3]\n\n        # checking if new tags were added to the bookmark\n        self.assertTrue(split_and_test_membership(new_tags, from_db))\n        # checking if old tags still exist\n        self.assertTrue(split_and_test_membership(old_tags, from_db))\n\n    def test_append_tag_at_all_indices(self):\n        for bookmark in self.bookmarks:\n            _add_rec(self.bdb, *bookmark)\n\n        # tags to add\n        new_tags = \",foo,bar,baz\"\n        # record of original tags for each bookmark\n        old_tagsets = {\n            i: self.bdb.get_rec_by_id(i)[3]\n            for i in inclusive_range(1, len(self.bookmarks))\n        }\n\n        with mock.patch(\"builtins.input\", return_value=\"y\"):\n            self.bdb.append_tag_at_index(0, new_tags)\n            # updated tags for each bookmark\n            from_db = [\n                (i, self.bdb.get_rec_by_id(i)[3])\n                for i in inclusive_range(1, len(self.bookmarks))\n            ]\n            for index, tagset in from_db:\n                # checking if new tags added to bookmark\n                self.assertTrue(split_and_test_membership(new_tags, tagset))\n                # checking if old tags still exist for bookmark\n                self.assertTrue(split_and_test_membership(old_tagsets[index], tagset))\n\n    def test_delete_tag_at_index(self):\n        # adding bookmarks\n        for bookmark in self.bookmarks:\n            _add_rec(self.bdb, *bookmark)\n\n        get_tags_at_idx = lambda i: self.bdb.get_rec_by_id(i)[3]\n        # list of two-tuples, each containing bookmark index and corresponding tags\n        tags_by_index = [\n            (i, get_tags_at_idx(i)) for i in inclusive_range(1, len(self.bookmarks))\n        ]\n\n        for i, tags in tags_by_index:\n            # get the first tag from the bookmark\n            to_delete = re.match(\",.*?,\", tags).group(0)\n            self.bdb.delete_tag_at_index(i, to_delete)\n            # get updated tags from db\n            from_db = get_tags_at_idx(i)\n            self.assertNotIn(to_delete, from_db)\n\n    def test_search_keywords_and_filter_by_tags(self):\n        # adding bookmark\n        for bookmark in self.bookmarks:\n            _add_rec(self.bdb, *bookmark)\n\n        with mock.patch(\"buku.prompt\"):\n            expected = [\n                (\n                    3,\n                    \"http://example.com/\",\n                    \"test\",\n                    \",es,est,tes,test,\",\n                    \"a case for replace_tag test\",\n                    0,\n                )\n            ]\n            results = self.bdb.search_keywords_and_filter_by_tags(\n                [\"News\", \"case\"],\n                False,\n                False,\n                False,\n                [\"est\"],\n            )\n            self.assertIn(expected[0], results)\n            expected = [\n                (\n                    3,\n                    \"http://example.com/\",\n                    \"test\",\n                    \",es,est,tes,test,\",\n                    \"a case for replace_tag test\",\n                    0,\n                ),\n                (\n                    2,\n                    \"http://www.zażółćgęśląjaźń.pl/\",\n                    \"ZAŻÓŁĆ\",\n                    \",gęślą,jaźń,zażółć,\",\n                    \"Testing UTF-8, zażółć gęślą jaźń.\",\n                    0,\n                ),\n            ]\n            results = self.bdb.search_keywords_and_filter_by_tags(\n                [\"UTF-8\", \"case\"],\n                False,\n                False,\n                False,\n                \"jaźń, test\",\n            )\n            self.assertIn(expected[0], results)\n            self.assertIn(expected[1], results)\n\n    def test_searchdb(self):\n        # adding bookmarks\n        for bookmark in self.bookmarks:\n            _add_rec(self.bdb, *bookmark)\n\n        get_first_tag = lambda x: \"\".join(x[2].split(\",\")[:2])\n        for i, bookmark in enumerate(self.bookmarks):\n            tag_search = get_first_tag(bookmark)\n            # search by the domain name for url\n            url_search = re.match(r\"https?://(.*)?\\..*\", bookmark[0]).group(1)\n            title_search = bookmark[1]\n            # Expect a five-tuple containing all bookmark data\n            # db index, URL, title, tags, description\n            expected = [(i + 1,) + tuple(bookmark)]\n            expected[0] += tuple([0])\n            # search db by tag, url (domain name), and title\n            for keyword in (tag_search, url_search, title_search):\n                with mock.patch(\"buku.prompt\"):\n                    # search by keyword\n                    results = self.bdb.searchdb([keyword])\n                    self.assertEqual(results, expected)\n\n    def test_search_by_tag(self):\n        # adding bookmarks\n        for bookmark in self.bookmarks:\n            _add_rec(self.bdb, *bookmark)\n\n        with mock.patch(\"buku.prompt\"):\n            get_first_tag = lambda x: \"\".join(x[2].split(\",\")[:2])\n            for i, bookmark in enumerate(self.bookmarks):\n                # search for bookmark with a tag that is known to exist\n                results = self.bdb.search_by_tag(get_first_tag(bookmark))\n                # Expect a five-tuple containing all bookmark data\n                # db index, URL, title, tags, description\n                expected = [(i + 1,) + tuple(bookmark)]\n                expected[0] += tuple([0])\n                self.assertEqual(results, expected)\n\n    @pytest.mark.slow\n    @pytest.mark.vcr(\"tests/vcr_cassettes/test_search_by_multiple_tags_search_any.yaml\")\n    def test_search_by_multiple_tags_search_any(self):\n        # adding bookmarks\n        for bookmark in self.bookmarks:\n            self.bdb.add_rec(*bookmark)\n\n        new_bookmark = [\n            \"https://newbookmark.com\",\n            \"New Bookmark\",\n            parse_tags([\"test,old,new\"]),\n            \"additional bookmark to test multiple tag search\",\n            0,\n        ]\n\n        self.bdb.add_rec(*new_bookmark)\n\n        with mock.patch(\"buku.prompt\"):\n            # search for bookmarks matching ANY of the supplied tags\n            results = self.bdb.search_by_tag(\"test, old\")\n            # Expect a list of five-element tuples containing all bookmark data\n            # db index, URL, title, tags, description, ordered by records with\n            # the most number of matches.\n            expected = [\n                (\n                    4,\n                    \"https://newbookmark.com\",\n                    \"New Bookmark\",\n                    parse_tags([\",test,old,new,\"]),\n                    \"additional bookmark to test multiple tag search\",\n                    0,\n                ),\n                (\n                    1,\n                    \"http://slashdot.org\",\n                    \"SLASHDOT\",\n                    parse_tags([\",news,old,\"]),\n                    \"News for old nerds, stuff that doesn't matter\",\n                    0,\n                ),\n                (\n                    3,\n                    \"http://example.com/\",\n                    \"test\",\n                    \",es,est,tes,test,\",\n                    \"a case for replace_tag test\",\n                    0,\n                ),\n            ]\n            self.assertEqual(results, expected)\n\n    @pytest.mark.slow\n    @pytest.mark.vcr(\"tests/vcr_cassettes/test_search_by_multiple_tags_search_all.yaml\")\n    def test_search_by_multiple_tags_search_all(self):\n        # adding bookmarks\n        for bookmark in self.bookmarks:\n            self.bdb.add_rec(*bookmark)\n\n        new_bookmark = [\n            \"https://newbookmark.com\",\n            \"New Bookmark\",\n            parse_tags([\"test,old,new\"]),\n            \"additional bookmark to test multiple tag search\",\n        ]\n\n        self.bdb.add_rec(*new_bookmark)\n\n        with mock.patch(\"buku.prompt\"):\n            # search for bookmarks matching ALL of the supplied tags\n            results = self.bdb.search_by_tag(\"test + old\")\n            # Expect a list of five-element tuples containing all bookmark data\n            # db index, URL, title, tags, description\n            expected = [\n                (\n                    4,\n                    \"https://newbookmark.com\",\n                    \"New Bookmark\",\n                    parse_tags([\",test,old,new,\"]),\n                    \"additional bookmark to test multiple tag search\",\n                    0,\n                )\n            ]\n            self.assertEqual(results, expected)\n\n    def test_search_by_tags_enforces_space_seprations_search_all(self):\n\n        bookmark1 = [\n            \"https://bookmark1.com\",\n            \"Bookmark One\",\n            parse_tags([\"tag, two,tag+two\"]),\n            \"test case for bookmark with '+' in tag\",\n        ]\n\n        bookmark2 = [\n            \"https://bookmark2.com\",\n            \"Bookmark Two\",\n            parse_tags([\"tag,two, tag-two\"]),\n            \"test case for bookmark with hyphenated tag\",\n        ]\n\n        _add_rec(self.bdb, *bookmark1)\n        _add_rec(self.bdb, *bookmark2)\n\n        with mock.patch(\"buku.prompt\"):\n            # check that space separation for ' + ' operator is enforced\n            results = self.bdb.search_by_tag(\"tag+two\")\n            # Expect a list of five-element tuples containing all bookmark data\n            # db index, URL, title, tags, description\n            expected = [\n                (\n                    1,\n                    \"https://bookmark1.com\",\n                    \"Bookmark One\",\n                    parse_tags([\",tag,two,tag+two,\"]),\n                    \"test case for bookmark with '+' in tag\",\n                    0,\n                )\n            ]\n            self.assertEqual(results, expected)\n            results = self.bdb.search_by_tag(\"tag + two\")\n            # Expect a list of five-element tuples containing all bookmark data\n            # db index, URL, title, tags, description\n            expected = [\n                (\n                    1,\n                    \"https://bookmark1.com\",\n                    \"Bookmark One\",\n                    parse_tags([\",tag,two,tag+two,\"]),\n                    \"test case for bookmark with '+' in tag\",\n                    0,\n                ),\n                (\n                    2,\n                    \"https://bookmark2.com\",\n                    \"Bookmark Two\",\n                    parse_tags([\",tag,two,tag-two,\"]),\n                    \"test case for bookmark with hyphenated tag\",\n                    0,\n                ),\n            ]\n            self.assertEqual(results, expected)\n\n    def test_search_by_tags_exclusion(self):\n        # adding bookmarks\n        for bookmark in self.bookmarks:\n            _add_rec(self.bdb, *bookmark)\n\n        new_bookmark = [\n            \"https://newbookmark.com\",\n            \"New Bookmark\",\n            parse_tags([\"test,old,new\"]),\n            \"additional bookmark to test multiple tag search\",\n        ]\n\n        _add_rec(self.bdb, *new_bookmark)\n\n        with mock.patch(\"buku.prompt\"):\n            # search for bookmarks matching ANY of the supplied tags\n            # while excluding bookmarks from results that match a given tag\n            results = self.bdb.search_by_tag(\"test, old - est\")\n            # Expect a list of five-element tuples containing all bookmark data\n            # db index, URL, title, tags, description\n            expected = [\n                (\n                    4,\n                    \"https://newbookmark.com\",\n                    \"New Bookmark\",\n                    parse_tags([\",test,old,new,\"]),\n                    \"additional bookmark to test multiple tag search\",\n                    0,\n                ),\n                (\n                    1,\n                    \"http://slashdot.org\",\n                    \"SLASHDOT\",\n                    parse_tags([\",news,old,\"]),\n                    \"News for old nerds, stuff that doesn't matter\",\n                    0,\n                ),\n            ]\n            self.assertEqual(results, expected)\n\n    @pytest.mark.vcr(\"tests/vcr_cassettes/test_search_by_tags_enforces_space_seprations_exclusion.yaml\")\n    def test_search_by_tags_enforces_space_seprations_exclusion(self):\n\n        bookmark1 = [\n            \"https://bookmark1.com\",\n            \"Bookmark One\",\n            parse_tags([\"tag, two,tag+two\"]),\n            \"test case for bookmark with '+' in tag\",\n        ]\n\n        bookmark2 = [\n            \"https://bookmark2.com\",\n            \"Bookmark Two\",\n            parse_tags([\"tag,two, tag-two\"]),\n            \"test case for bookmark with hyphenated tag\",\n        ]\n\n        bookmark3 = [\n            \"https://bookmark3.com\",\n            \"Bookmark Three\",\n            parse_tags([\"tag, tag three\"]),\n            \"second test case for bookmark with hyphenated tag\",\n        ]\n\n        self.bdb.add_rec(*bookmark1)\n        self.bdb.add_rec(*bookmark2)\n        self.bdb.add_rec(*bookmark3)\n\n        with mock.patch(\"buku.prompt\"):\n            # check that space separation for ' - ' operator is enforced\n            results = self.bdb.search_by_tag(\"tag-two\")\n            # Expect a list of five-element tuples containing all bookmark data\n            # db index, URL, title, tags, description\n            expected = [\n                (\n                    2,\n                    \"https://bookmark2.com\",\n                    \"Bookmark Two\",\n                    parse_tags([\",tag,two,tag-two,\"]),\n                    \"test case for bookmark with hyphenated tag\",\n                    0,\n                ),\n            ]\n            self.assertEqual(results, expected)\n            results = self.bdb.search_by_tag(\"tag - two\")\n            # Expect a list of five-element tuples containing all bookmark data\n            # db index, URL, title, tags, description\n            expected = [\n                (\n                    3,\n                    \"https://bookmark3.com\",\n                    \"Bookmark Three\",\n                    parse_tags([\",tag,tag three,\"]),\n                    \"second test case for bookmark with hyphenated tag\",\n                    0,\n                ),\n            ]\n            self.assertEqual(results, expected)\n\n    def test_search_and_open_in_browser_by_range(self):\n        # adding bookmarks\n        for bookmark in self.bookmarks:\n            _add_rec(self.bdb, *bookmark)\n\n        # simulate user input, select range of indices 1-3\n        index_range = \"1-%s\" % len(self.bookmarks)\n        with mock.patch(\"builtins.input\", side_effect=[index_range]):\n            with mock.patch(\"buku.browse\") as mock_browse:\n                try:\n                    # search the db with keywords from each bookmark\n                    # searching using the first tag from bookmarks\n                    get_first_tag = lambda x: x[2].split(\",\")[1]\n                    results = self.bdb.searchdb(\n                        [get_first_tag(bm) for bm in self.bookmarks]\n                    )\n                    prompt(self.bdb, results)\n                except StopIteration:\n                    # catch exception thrown by reaching the end of the side effect iterable\n                    pass\n\n                # collect arguments passed to browse\n                arg_list = [args[0] for args, _ in mock_browse.call_args_list]\n                # expect a list of one-tuples that are bookmark URLs\n                expected = [x[0] for x in self.bookmarks]\n                # checking if browse called with expected arguments\n                self.assertEqual(arg_list, expected)\n\n    @pytest.mark.slow\n    @pytest.mark.vcr(\"tests/vcr_cassettes/test_search_and_open_all_in_browser.yaml\")\n    def test_search_and_open_all_in_browser(self):\n        # adding bookmarks\n        for bookmark in self.bookmarks:\n            self.bdb.add_rec(*bookmark)\n\n        # simulate user input, select 'a' to open all bookmarks in results\n        with mock.patch(\"builtins.input\", side_effect=[\"a\"]):\n            with mock.patch(\"buku.browse\") as mock_browse:\n                try:\n                    # search the db with keywords from each bookmark\n                    # searching using the first tag from bookmarks\n                    get_first_tag = lambda x: x[2].split(\",\")[1]\n                    results = self.bdb.searchdb(\n                        [get_first_tag(bm) for bm in self.bookmarks[:2]]\n                    )\n                    prompt(self.bdb, results)\n                except StopIteration:\n                    # catch exception thrown by reaching the end of the side effect iterable\n                    pass\n\n                # collect arguments passed to browse\n                arg_list = [args[0] for args, _ in mock_browse.call_args_list]\n                # expect a list of one-tuples that are bookmark URLs\n                expected = [x[0] for x in self.bookmarks][:2]\n                # checking if browse called with expected arguments\n                self.assertEqual(arg_list, expected)\n\n    def test_delete_rec(self):\n        # adding bookmark and getting index\n        _add_rec(self.bdb, *self.bookmarks[0])\n        index = self.bdb.get_rec_id(self.bookmarks[0][0])\n        # deleting bookmark\n        self.bdb.delete_rec(index)\n        # asserting it doesn't exist\n        from_db = self.bdb.get_rec_by_id(index)\n        self.assertIsNone(from_db)\n\n    def test_delete_rec_yes(self):\n        # checking that \"y\" response causes delete_rec to return True\n        with mock.patch(\"builtins.input\", return_value=\"y\"):\n            self.assertTrue(self.bdb.delete_rec(0))\n\n    def test_delete_rec_no(self):\n        # checking that non-\"y\" response causes delete_rec to return None\n        with mock.patch(\"builtins.input\", return_value=\"n\"):\n            self.assertFalse(self.bdb.delete_rec(0))\n\n    def test_cleardb(self):\n        # adding bookmarks\n        _add_rec(self.bdb, *self.bookmarks[0])\n        # deleting all bookmarks\n        with mock.patch(\"builtins.input\", return_value=\"y\"):\n            self.bdb.cleardb()\n        # assert table has been dropped\n        assert self.bdb.get_rec_by_id(0) is None\n\n    def test_replace_tag(self):\n        indices = []\n        for bookmark in self.bookmarks:\n            # adding bookmark, getting index\n            _add_rec(self.bdb, *bookmark)\n            index = self.bdb.get_rec_id(bookmark[0])\n            indices += [index]\n\n        # replacing tags\n        with mock.patch(\"builtins.input\", return_value=\"y\"):\n            self.bdb.replace_tag(\"news\", [\"__01\"])\n        with mock.patch(\"builtins.input\", return_value=\"y\"):\n            self.bdb.replace_tag(\"zażółć\", [\"__02,__03\"])\n\n        # replacing tag which is also a substring of other tag\n        with mock.patch(\"builtins.input\", return_value=\"y\"):\n            self.bdb.replace_tag(\"es\", [\"__04\"])\n\n        # removing tags\n        with mock.patch(\"builtins.input\", return_value=\"y\"):\n            self.bdb.replace_tag(\"gęślą\")\n        with mock.patch(\"builtins.input\", return_value=\"y\"):\n            self.bdb.replace_tag(\"old\")\n\n        # removing non-existent tag\n        with mock.patch(\"builtins.input\", return_value=\"y\"):\n            self.bdb.replace_tag(\"_\")\n\n        # removing nonexistent tag which is also a substring of other tag\n        with mock.patch(\"builtins.input\", return_value=\"y\"):\n            self.bdb.replace_tag(\"e\")\n\n        for url, title, _, _ in self.bookmarks:\n            # retrieving from db\n            index = self.bdb.get_rec_id(url)\n            from_db = self.bdb.get_rec_by_id(index)\n            # asserting tags were replaced\n            if title == \"SLASHDOT\":\n                self.assertEqual(from_db[3], parse_tags([\"__01\"]))\n            elif title == \"ZAŻÓŁĆ\":\n                self.assertEqual(from_db[3], parse_tags([\"__02,__03,jaźń\"]))\n            elif title == \"test\":\n                self.assertEqual(from_db[3], parse_tags([\"test,tes,est,__04\"]))\n\n    # def test_browse_by_index(self):\n    # self.fail()\n\n    def test_close_quit(self):\n        # quitting with no args\n        try:\n            self.bdb.close_quit()\n        except SystemExit as err:\n            self.assertEqual(err.args[0], 0)\n        # quitting with custom arg\n        try:\n            self.bdb.close_quit(1)\n        except SystemExit as err:\n            self.assertEqual(err.args[0], 1)\n\n    # def test_import_bookmark(self):\n    # self.fail()\n\n\n@pytest.mark.parametrize('status', [None, 200, 302, 308, 404, 500])\n@pytest.mark.parametrize('fetch, url_redirect, tag_redirect, tag_error, del_error', [\n    (False, False, False, False, None),                # offline\n    (True, True, False, False, None),                  # url-redirect\n    (True, False, True, True, None),                   # tag-redirect, tag-error\n    (True, True, 'http-{}', 'error:{}', None),         # url-redirect, fetch-tags (custom patterns)\n    (True, True, 'redirect', 'error', None),           # ... (patterns without codes)\n    (True, True, 'redirect', False, range(400, 600)),  # del-error (any errors)\n    (True, True, 'redirect', 'error', {404}),          # ... (some errors)\n])\ndef test_add_rec_fetch(bukuDb, caplog, fetch, url_redirect, tag_redirect, tag_error, del_error, status):\n    '''Testing add_rec() behaviour with fetch-status params'''\n    title_in, title, desc = 'Custom Title', 'Fetched Title', 'Fetched description.'\n    tags_in, url_in, url_new = ',custom,tags,', 'https://example.com', 'https://example.com/redirect'\n    url = (url_new if status in PERMANENT_REDIRECTS else url_in)\n    bdb = bukuDb()\n    with mock_fetch(url=url, title=title, desc=desc, fetch_status=status) as fetch_data:\n        index = bdb.add_rec(url=url_in, title_in=title_in, tags_in=tags_in, fetch=fetch,\n                            url_redirect=url_redirect, tag_redirect=tag_redirect,\n                            tag_error=tag_error, del_error=del_error)\n\n    # del-error?\n    if del_error and (not status or status in del_error):\n        assert index is None\n        assert bdb.get_max_id() is None\n        err = ('Network error' if not status else 'HTTP error {}'.format(status))\n        assert caplog.record_tuples == [('root', 40, 'add_rec(): '+err)]\n        return\n    rec = bdb.get_rec_by_id(index)\n\n    # offline?\n    if not fetch:\n        fetch_data.assert_not_called()\n        assert (rec.url, rec.title, rec.desc) == (url_in, title_in, '')\n        assert _tagset(rec.tags_raw) == _tagset(tags_in)\n        return\n\n    # url-redirect?\n    if url_redirect and status in PERMANENT_REDIRECTS:\n        assert rec.url == url_new\n    else:\n        assert rec.url == url_in\n\n    # custom title, fetched description\n    assert (rec.title, rec.desc) == (title_in, desc), 'custom title overrides fetched title'\n\n    # fetch-tags?\n    _tags = _tagset(tags_in)\n    if tag_redirect and status in PERMANENT_REDIRECTS:\n        _tags |= {('http:{}' if tag_redirect is True else tag_redirect).format(status).lower()}\n    if tag_error and (status or 0) >= 400:\n        _tags |= {('http:{}' if tag_error is True else tag_error).format(status).lower()}\n    assert _tagset(rec.tags) == _tags\n\n\n@pytest.mark.parametrize('status, tags_fetched, tags_in, tags_except, expected', [\n    (None, None, 'foo,qux,foo bar,bar,baz', 'except,bar,foo,', ',baz,foo bar,qux,'),\n    (200, '', 'foo,qux,foo bar,bar,baz', None, ',bar,baz,foo,foo bar,qux,'),\n    (200, 'there,have been,some,tags,fetched', None,\n     'except,bar,tags,there,foo', ',fetched,have been,some,'),\n    (200, 'there,have been,some,tags,fetched', 'foo,qux,foo bar,bar,baz',\n     'except,bar,tags,there,foo', ',baz,fetched,foo bar,have been,qux,some,'),\n    (404, None, 'foo,foo bar,qux,bar,baz', 'except,bar,foo', ',baz,foo bar,http:error,qux,'),\n    (301, 'there,have been,some,tags,fetched', 'foo,foo bar,qux,bar,baz',\n     'except,bar,tags,there,foo', ',baz,fetched,foo bar,have been,http:redirect,qux,some,'),\n    (308, 'there,have been,some,tags,fetched', 'foo,foo bar,qux,bar,baz',\n     'except,http:redirect,bar,tags,there,foo', ',baz,fetched,foo bar,have been,qux,some,'),\n])\ndef test_add_rec_tags(bukuDb, caplog, status, tags_fetched, tags_in, tags_except, expected):\n    '''Testing add_rec() behaviour with tags params'''\n    url, keywords = 'https://example.com', (',fetched,tags,' if tags_fetched is None else tags_fetched)\n    bdb = bukuDb()\n    with mock_fetch(url=url, title='Title', keywords=keywords, fetch_status=status):\n        index = bdb.add_rec(url=url, fetch=status is not None, tags_in=tags_in, tags_except=tags_except,\n                            tags_fetch=tags_fetched is not None, tag_redirect='http:redirect', tag_error='http:error')\n    rec = bdb.get_rec_by_id(index)\n    assert rec.tags_raw == expected\n\n\n@pytest.mark.parametrize('index', [1, {2, 3}, None])\n@pytest.mark.parametrize('export_on', [None, PERMANENT_REDIRECTS, range(400, 600), PERMANENT_REDIRECTS | {404}])\n@pytest.mark.parametrize('url_in, title_in, tags_in, url_redirect, tag_redirect, tag_error, del_error', [\n    (None, None, None, False, False, False, None),                                          # fetched title/desc, no network test\n    (None, 'Custom Title', ',custom,tags,', False, False, False, None),                     # title, tags, no network test\n    ('http://custom.url', None, None, False, False, False, None),                           # url, fetched title/desc, no network test\n    ('http://custom.url', 'Custom Title', ',custom,tags,', False, False, False, None),      # url, title, tags, no network test\n    (None, 'Custom Title', '+,custom,tags,', True, False, False, None),                     # title, +tags, url-redirect\n    ('http://custom.url', 'Custom Title', '+,custom,tags,', False, True, True, None),       # url, title, +tags, fetch-tags\n    (None, 'Custom Title', None, True, 'http-{}', 'error:{}', None),                        # title, url-redirect, fetch-tags (custom)\n    (None, None, '-,initial%,', True, 'redirect', 'error', None),                           # -tags, url-redirect, fetch-tags (no codes)\n    ('http://custom.url', 'Custom Title', None, True, 'redirect', False, range(400, 600)),  # url, title, url-redirect, del-error\n    (None, None, ',custom,tags,', True, 'redirect', 'error', {404}),                        # tags, url-redirect, fetch-tags, del-error\n])\ndef test_update_rec_fetch(bukuDb, caplog, url_in, title_in, tags_in, url_redirect, tag_redirect, tag_error, del_error, export_on, index):\n    '''Testing update_rec() behaviour with fetch-status params'''\n    # redirected URL, nonexistent page, nonexistend domain\n    urls = {\n        'http://wikipedia.net': {'fetch_status': 301, 'url': 'https://www.wikipedia.org', 'title': 'Wikipedia',\n                                 'desc': 'Wikipedia is a free online encyclopedia, created and edited blah blah'},\n        'https://python.org/notfound': {'fetch_status': 404, 'title': 'Welcome to Python.org',\n                                        'desc': 'The official home of the Python Programming Language'},\n        'http://nonexistent.url': {'fetch_status': None},  # unable to resolve host address\n    }\n    # for the URL override\n    custom_url = {'fetch_status': 200, 'title': 'Fetched Title', 'desc': 'Fetched description.'}\n\n    def custom_fetch(url, http_head=False):\n        data = dict(urls.get(url, custom_url))\n        _url = data.pop('url', url)\n        return FetchResult(url_in or _url, **data)\n\n    # computed test parameters\n    title_initial, tags_initial, desc = 'Initial Title', ',initial%,tags,', 'Initial description.'\n    fetch_title = title_in is tags_in is None  # when no custom params are passed (except for URL), titles are fetched\n    network_test = url_redirect or tag_redirect or tag_error or del_error or export_on or fetch_title\n    indices = ({index} if isinstance(index, int) else index or range(1, len(urls)+1))\n    tags = _tagset(tags_in if (tags_in or '').startswith(',') else tags_initial)\n    if not (tags_in or ',').startswith(','):\n        tags = (tags | _tagset(tags_in[1:]) if tags_in.startswith('+') else tags - _tagset(tags_in[1:]))\n\n    # setup\n    bdb = bukuDb()\n    for url_initial in urls:\n        _add_rec(bdb, url_initial, title_in=title_initial, tags_in=tags_initial, desc=desc)\n    assert bdb.get_max_id() == len(urls), 'expecting correct setup'\n    with mock_fetch(custom_fetch) as fetch_data:\n        with mock.patch('buku.read_in', return_value='y'):\n            ok = bdb.update_rec(index=index, url=url_in, title_in=title_in, tags_in=tags_in,\n                                url_redirect=url_redirect, tag_redirect=tag_redirect,\n                                tag_error=tag_error, del_error=del_error, export_on=export_on)\n    recs = bdb.get_rec_all()\n\n    # custom URL on multiple records?\n    if url_in and len(indices) != 1:\n        assert not ok, 'expected to fail'\n        assert caplog.record_tuples == [('root', 40, 'All URLs cannot be same')]\n        fetch_data.assert_not_called()\n        assert recs == [BookmarkVar(id, url, title_initial, tags_initial, desc)\n                        for id, url in enumerate(urls, start=1)]\n        return\n    assert ok, 'expected to succeed'\n\n    # offline?\n    if not network_test and not (url_in and title_in is None):\n        _tags = ',' + ','.join(sorted(tags)) + ','\n        fetch_data.assert_not_called()\n        for rec, url in zip(recs, urls):\n            if rec.id in indices:\n                assert rec == BookmarkVar(rec.id, url_in or url, title_in or title_initial, _tags, desc)\n            else:\n                assert rec == BookmarkVar(rec.id, url, title_initial, tags_initial, desc)\n        return\n\n    # export-on (given HTTP codes)?\n    if not export_on:\n        assert bdb._to_export is None, f'expected no to_export backup: {bdb._to_export}'\n    else:\n        assert isinstance(bdb._to_export, dict), f'to_export backup is not a dict: {bdb._to_export}'\n    to_export = dict(bdb._to_export or {})\n    _urls, _recs = set(urls), {x.url: x for x in recs}\n\n    # one fetch per index\n    assert fetch_data.call_count == len(indices), f'expected {len(indices)} fetches, done {fetch_data.call_count}'\n    for call in fetch_data.call_args_list:\n        # determining fetched, original and redirected URLs, along with fetched data\n        url = call.args[0]\n        url_old = url if not url_in else list(urls)[index-1]  # url_in applies to a single record\n        _urls -= {url_old}\n        data = urls.get(url, custom_url)\n        url_new = (url if not url_redirect else data.get('url', url))\n        rec = _recs.pop(url_new, None)\n        status = data.get('fetch_status')\n\n        # del-error? export-on?\n        old = to_export.pop(url_new, None)\n        if not export_on or status not in export_on:\n            assert old is None, f'{url_old}: backup not expected'\n        if del_error and status in del_error:\n            assert rec is None, f'{url_old}: HTTP error {status}, should delete'\n            if export_on and status in export_on:\n                assert isinstance(old, BookmarkVar), f'{url_old}: should backup old record'\n                assert (old.url, old.title, old.tags_raw, old.desc) == (url_old, title_initial, tags_initial, desc)\n            continue\n        if export_on and status in export_on:\n            assert old == url_old, f'{url_old}: should backup old url on redirect'\n\n        # url-redirect?\n        if url_redirect and status in PERMANENT_REDIRECTS:\n            assert url_new != url_old, f'{url_old}: redirect expected'\n            assert rec.url == url_new, f'{url_old}: should replace with {url_new}'\n        else:\n            assert url_new == rec.url, f'{url_old}: redirect not expected'\n            assert url_new == (url_in or url_old), f'{url_old}: URL should not be changed'\n\n        # title\n        if title_in or (fetch_title and 'title' in data):\n            assert rec.title == (title_in or data['title']), f'{url_old}: should update title'\n        else:\n            assert rec.title == title_initial, f'{url_old}: should not update title'\n\n        # description\n        if fetch_title and 'desc' in data:\n            assert rec.desc == data['desc'], f'{url_old}: should update description'\n        else:\n            assert rec.desc == desc, f'{url_old}: should not update description'\n\n        # tags (+fetch-tags)\n        _tags = set()\n        if tag_redirect and status in PERMANENT_REDIRECTS:\n            _tags |= {('http:{}' if tag_redirect is True else tag_redirect).format(status).lower()}\n        elif tag_error and status in range(400, 600):\n            _tags |= {('http:{}' if tag_error is True else tag_error).format(status).lower()}\n        _tags_str = ',' + ','.join(sorted(_tags)) + ','\n        assert _tagset(rec.tags) == tags | _tags, f'{url_old}: [{tags_initial} | {tags_in or \",\"} | {_tags_str}] -> {rec.tags}'\n\n    # other records should not have been affected (other than possibly indices)\n    assert not to_export, f'unexpected to_export backup: {to_export}'\n    assert set(_recs) == _urls\n    for rec in _recs.values():\n        assert rec == BookmarkVar(rec.id, rec.url, title_initial, tags_initial, desc)\n\n\n@pytest.mark.parametrize('ext, expected', [\n    ('db', [(1, 'http://custom.url', 'Fetched Title (DELETED)', ',', 'Fetched description.'),\n            (2, 'https://www.wikipedia.org', 'Wikipedia (OLD URL = http://wikipedia.net)', ',http:301,', 'Wikipedia is a free...'),\n            (3, 'https://python.org/notfound', 'Welcome to Python.org', ',http:404,', 'The official home...')]),\n    ('md', ['- [Fetched Title (DELETED)](http://custom.url)',\n            '- [Wikipedia (OLD URL = http://wikipedia.net)](https://www.wikipedia.org) <!-- TAGS: http:301 -->',\n            '- [Welcome to Python.org](https://python.org/notfound) <!-- TAGS: http:404 -->']),\n    ('org', ['* [[http://custom.url][Fetched Title (DELETED)]]',\n             '* [[https://www.wikipedia.org][Wikipedia (OLD URL = http://wikipedia.net)]] :http_301:',\n             '* [[https://python.org/notfound][Welcome to Python.org]] :http_404:']),\n    ('xbel', ['<?xml version=\"1.0\" encoding=\"UTF-8\"?>',\n              '<!DOCTYPE xbel PUBLIC \"+//IDN python.org//DTD XML Bookmark Exchange Language 1.0//EN//XML\"'\n                                   ' \"http://pyxml.sourceforge.net/topics/dtds/xbel.dtd\">',\n              '',\n              '<xbel version=\"1.0\">',\n              '    <bookmark href=\"http://custom.url\">',\n              '        <title>Fetched Title (DELETED)</title>',\n              '        <desc>Fetched description.</desc>',\n              '    </bookmark>',\n              '    <bookmark href=\"https://www.wikipedia.org\" TAGS=\"http:301\">',\n              '        <title>Wikipedia (OLD URL = http://wikipedia.net)</title>',\n              '        <desc>Wikipedia is a free...</desc>',\n              '    </bookmark>',\n              '    <bookmark href=\"https://python.org/notfound\" TAGS=\"http:404\">',\n              '        <title>Welcome to Python.org</title>',\n              '        <desc>The official home...</desc>',\n              '    </bookmark>',\n              '</xbel>']),\n    ('html', ['<!DOCTYPE NETSCAPE-Bookmark-file-1>', '',\n              '<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\">',\n              '<TITLE>Bookmarks</TITLE>', '<H1>Bookmarks</H1>', '',\n              '<DL><p>',\n              '    <DT><H3 ADD_DATE=\"{0}\" LAST_MODIFIED=\"{0}\" PERSONAL_TOOLBAR_FOLDER=\"true\">buku bookmarks</H3>',\n              '    <DL><p>',\n              '        <DT><A HREF=\"http://custom.url\" ADD_DATE=\"{0}\" LAST_MODIFIED=\"{0}\">Fetched Title (DELETED)</A>',\n              '        <DD>Fetched description.',\n              '        <DT><A HREF=\"https://www.wikipedia.org\" ADD_DATE=\"{0}\" LAST_MODIFIED=\"{0}\" '\n                             'TAGS=\"http:301\">Wikipedia (OLD URL = http://wikipedia.net)</A>',\n              '        <DD>Wikipedia is a free...',\n              '        <DT><A HREF=\"https://python.org/notfound\" ADD_DATE=\"{0}\" LAST_MODIFIED=\"{0}\" '\n                             'TAGS=\"http:404\">Welcome to Python.org</A>',\n              '        <DD>The official home...',\n              '    </DL><p>',\n              '</DL><p>']),\n    ('rss', ['<feed xmlns=\"http://www.w3.org/2005/Atom\">',\n             '    <title>Bookmarks</title>',\n             '    <generator uri=\"https://github.com/jarun/buku\">buku</generator>',\n             '    <entry>',\n             '        <title>Fetched Title (DELETED)</title>',\n             '        <link href=\"http://custom.url\" rel=\"alternate\" type=\"text/html\"/>',\n             '        <id>1</id>',  # deleted entry, pre-deletion ID\n             '        <content type=\"html\"> <![CDATA[ <p>Fetched description.</p> ]]> </content>',\n             '    </entry>',\n             '    <entry>',\n             '        <title>Wikipedia (OLD URL = http://wikipedia.net)</title>',\n             '        <link href=\"https://www.wikipedia.org\" rel=\"alternate\" type=\"text/html\"/>',\n             '        <id>1</id>',  # updated entry, current ID\n             '        <category term=\"http:301\"/>',\n             '        <content type=\"html\"> <![CDATA[ <p>Wikipedia is a free...</p> ]]> </content>',\n             '    </entry>',\n             '    <entry>',\n             '        <title>Welcome to Python.org</title>',\n             '        <link href=\"https://python.org/notfound\" rel=\"alternate\" type=\"text/html\"/>',\n             '        <id>2</id>',\n             '        <category term=\"http:404\"/>',\n             '        <content type=\"html\"> <![CDATA[ <p>The official home...</p> ]]> </content>',\n             '    </entry>',\n             '</feed>']),\n    ('atom', ['<feed xmlns=\"http://www.w3.org/2005/Atom\">',\n              '    <title>Bookmarks</title>',\n              '    <generator uri=\"https://github.com/jarun/buku\">buku</generator>',\n              '    <entry>',\n              '        <title>Fetched Title (DELETED)</title>',\n              '        <link href=\"http://custom.url\" rel=\"alternate\" type=\"text/html\"/>',\n              '        <id>1</id>',  # deleted entry, pre-deletion ID\n              '        <content type=\"html\"> <![CDATA[ <p>Fetched description.</p> ]]> </content>',\n              '    </entry>',\n              '    <entry>',\n              '        <title>Wikipedia (OLD URL = http://wikipedia.net)</title>',\n              '        <link href=\"https://www.wikipedia.org\" rel=\"alternate\" type=\"text/html\"/>',\n              '        <id>1</id>',  # updated entry, current ID\n              '        <category term=\"http:301\"/>',\n              '        <content type=\"html\"> <![CDATA[ <p>Wikipedia is a free...</p> ]]> </content>',\n              '    </entry>',\n              '    <entry>',\n              '        <title>Welcome to Python.org</title>',\n              '        <link href=\"https://python.org/notfound\" rel=\"alternate\" type=\"text/html\"/>',\n              '        <id>2</id>',\n              '        <category term=\"http:404\"/>',\n              '        <content type=\"html\"> <![CDATA[ <p>The official home...</p> ]]> </content>',\n              '    </entry>',\n              '</feed>']),\n])\ndef test_export_on(bukuDb, ext, expected):\n    '''Testing exportdb() behaviour after update_rec() with export_on'''\n    outfile = TEST_TEMP_DIR_PATH + '/export-on.' + ext\n    bdb = bukuDb()\n    _add_rec(bdb, 'https://www.wikipedia.org', 'Wikipedia', ',http:301,', 'Wikipedia is a free...')\n    _add_rec(bdb, 'https://python.org/notfound', 'Welcome to Python.org', ',http:404,', 'The official home...')\n    _add_rec(bdb, 'https://nonexistent.url', 'Custom Title')                    # not exported\n    to_export = {'http://custom.url': BookmarkVar(1, 'http://custom.url', 'Fetched Title', ',', 'Fetched description.'),  # deleted\n                 'https://www.wikipedia.org': 'http://wikipedia.net',           # redirect\n                 'https://python.org/notfound': 'https://python.org/notfound'}  # unchanged\n    bdb._to_export = dict(to_export)\n    bdb.exportdb(outfile, None)\n    if ext == 'db':\n        assert bukuDb(dbfile=outfile).get_rec_all() == list(bookmark_vars(expected))\n    else:\n        with open(outfile, encoding='utf-8') as fout:\n            output = fout.read()\n        match = re.search('ADD_DATE=\"([0-9]+)\"', output)\n        timestamp = match and match.group(1)\n        assert output.splitlines() == [s.format(timestamp) for s in expected]\n\n\n@pytest.fixture(scope=\"function\")\ndef refreshdb_fixture():\n    # Setup\n    os.environ[\"XDG_DATA_HOME\"] = TEST_TEMP_DIR_PATH\n\n    # start every test from a clean state\n    rmdb()\n\n    bdb = BukuDb()\n\n    yield bdb\n\n    rmdb(bdb)\n    # Teardown\n    os.environ[\"XDG_DATA_HOME\"] = TEST_TEMP_DIR_PATH\n\n\n@pytest.mark.parametrize(\n    \"title_in, exp_res\",\n    [\n        [\"?\", \"Example Domain\"],\n        [None, \"Example Domain\"],\n        [\"\", \"Example Domain\"],\n        [\"random title\", \"Example Domain\"],\n    ],\n)\ndef test_refreshdb(refreshdb_fixture, title_in, exp_res):\n    bdb = refreshdb_fixture\n    args = [\"http://example.com\"]\n    if title_in:\n        args.append(title_in)\n    _add_rec(bdb, *args)\n    with mock_fetch(title=exp_res):\n        bdb.refreshdb(1, 1)\n    from_db = bdb.get_rec_by_id(1)\n    assert from_db[2] == exp_res, \"from_db: {}\".format(from_db)\n\n\n@pytest.fixture\ndef test_print_caplog(caplog):\n    caplog.handler.records.clear()\n    caplog.records.clear()\n    yield caplog\n\n\n@pytest.mark.parametrize(\n    \"kwargs, rec, exp_res\",\n    [\n        [{}, TEST_PRINT_REC, (True, [])],\n        [{\"is_range\": True}, TEST_PRINT_REC, (True, [])],\n        [{\"index\": 0}, TEST_PRINT_REC, (True, [])],\n        [{\"index\": -1}, TEST_PRINT_REC, (True, [])],\n        [{\"index\": -2}, TEST_PRINT_REC, (True, [])],\n        [{\"index\": 2}, TEST_PRINT_REC, (False, [(\"root\", 40, \"No matching index 2\")])],\n        [{\"low\": -1, \"high\": -1}, TEST_PRINT_REC, (True, [])],\n        [\n            {\"low\": -1, \"high\": -1, \"is_range\": True},\n            TEST_PRINT_REC,\n            (False, [(\"root\", 40, \"Negative range boundary\")]),\n        ],\n        [{\"low\": 0, \"high\": 0, \"is_range\": True}, TEST_PRINT_REC, (True, [])],\n        [{\"low\": 0, \"high\": 1, \"is_range\": True}, TEST_PRINT_REC, (True, [])],\n        [{\"low\": 0, \"high\": 2, \"is_range\": True}, TEST_PRINT_REC, (True, [])],\n        [{\"low\": 2, \"high\": 2, \"is_range\": True}, TEST_PRINT_REC, (True, [])],\n        [{\"low\": 2, \"high\": 3, \"is_range\": True}, TEST_PRINT_REC, (True, [])],\n        # empty database\n        [{\"is_range\": True}, None, (True, [])],\n        [{\"index\": 0}, None, (True, [(\"root\", 40, \"0 records\")])],\n        [{\"index\": -1}, None, (False, [(\"root\", 40, \"Empty database\")])],\n        [{\"index\": 1}, None, (False, [(\"root\", 40, \"No matching index 1\")])],\n        [{\"low\": -1, \"high\": -1}, TEST_PRINT_REC, (True, [])],\n        [\n            {\"low\": -1, \"high\": -1, \"is_range\": True},\n            None,\n            (False, [(\"root\", 40, \"Negative range boundary\")]),\n        ],\n        [{\"low\": 0, \"high\": 0, \"is_range\": True}, None, (True, [])],\n        [{\"low\": 0, \"high\": 1, \"is_range\": True}, None, (True, [])],\n        [{\"low\": 0, \"high\": 2, \"is_range\": True}, None, (True, [])],\n        [{\"low\": 2, \"high\": 2, \"is_range\": True}, None, (True, [])],\n        [{\"low\": 2, \"high\": 3, \"is_range\": True}, None, (True, [])],\n    ],\n)\ndef test_print_rec(bukuDb, kwargs, rec, exp_res, tmp_path, caplog):\n    bdb = bukuDb(dbfile=tmp_path / \"tmp.db\")\n    if rec:\n        _add_rec(bdb, *rec)\n    # run the function\n    assert (bdb.print_rec(**kwargs), caplog.record_tuples) == exp_res\n\n\ndef test_list_tags(capsys, bukuDb):\n    bdb = bukuDb()\n\n    # adding bookmarks\n    _add_rec(bdb, \"http://one.com\", \"\", parse_tags([\"cat,ant,bee,1\"]), \"\")\n    _add_rec(bdb, \"http://two.com\", \"\", parse_tags([\"Cat,Ant,bee,1\"]), \"\")\n    _add_rec(bdb, \"http://three.com\", \"\", parse_tags([\"Cat,Ant,3,Bee,2\"]), \"\")\n\n    # listing tags, asserting output\n    out, err = capsys.readouterr()\n    prompt(bdb, None, True, listtags=True)\n    out, err = capsys.readouterr()\n    exp_out = \"     1. 1 (2)\\n     2. 2 (1)\\n     3. 3 (1)\\n     4. ant (3)\\n     5. bee (3)\\n     6. cat (3)\\n\\n\"\n    assert out == exp_out\n    assert err == \"\"\n\n\ndef test_compactdb(bukuDb):\n    bdb = bukuDb()\n\n    # adding bookmarks\n    for bookmark in TEST_BOOKMARKS:\n        _add_rec(bdb, *bookmark)\n\n    # manually deleting 2nd index from db, calling compactdb\n    bdb.cur.execute(\"DELETE FROM bookmarks WHERE id = ?\", (2,))\n    bdb.compactdb(2)\n\n    # asserting bookmarks have correct indices\n    assert bdb.get_rec_by_id(1) == (\n        1,\n        \"http://slashdot.org\",\n        \"SLASHDOT\",\n        \",news,old,\",\n        \"News for old nerds, stuff that doesn't matter\",\n        0,\n    )\n    assert bdb.get_rec_by_id(2) == (\n        2,\n        \"http://example.com/\",\n        \"test\",\n        \",es,est,tes,test,\",\n        \"a case for replace_tag test\",\n        0,\n    )\n    assert bdb.get_rec_by_id(3) is None\n\n\n@pytest.mark.vcr()\n@pytest.mark.parametrize(\n    \"low, high, delay_commit, input_retval, exp_res\",\n    [\n        #  delay_commit, y input_retval\n        [0, 0, True, \"y\", (True, [])],\n        #  delay_commit, non-y input_retval\n        [\n            0,\n            0,\n            True,\n            \"x\",\n            (\n                False,\n                [tuple([x] + y + [0]) for x, y in zip(range(1, 4), TEST_BOOKMARKS)],\n            ),\n        ],\n        #  non delay_commit, y input_retval\n        [0, 0, False, \"y\", (True, [])],\n        #  non delay_commit, non-y input_retval\n        [\n            0,\n            0,\n            False,\n            \"x\",\n            (\n                False,\n                [tuple([x] + y + [0]) for x, y in zip(range(1, 4), TEST_BOOKMARKS)],\n            ),\n        ],\n    ],\n)\ndef test_delete_rec_range_and_delay_commit(\n    bukuDb, tmp_path, low, high, delay_commit, input_retval, exp_res\n):\n    \"\"\"test delete rec, range and delay commit.\"\"\"\n    bdb = bukuDb(dbfile=tmp_path / \"tmp.db\")\n    kwargs = {\"is_range\": True, \"low\": low, \"high\": high, \"delay_commit\": delay_commit}\n    kwargs[\"index\"] = 0\n\n    # Fill bookmark\n    for bookmark in TEST_BOOKMARKS:\n        _add_rec(bdb, *bookmark)\n\n    with mock.patch(\"builtins.input\", return_value=input_retval):\n        res = bdb.delete_rec(**kwargs)\n\n    assert (res, bdb.get_rec_all()) == exp_res\n\n    # teardown\n    os.environ[\"XDG_DATA_HOME\"] = TEST_TEMP_DIR_PATH\n\n\n@pytest.mark.parametrize(\n    \"index, delay_commit, input_retval\",\n    [\n        [-1, False, False],\n        [0, False, False],\n        [1, False, True],\n        [1, False, False],\n        [1, True, True],\n        [1, True, False],\n        [100, False, True],\n    ],\n)\ndef test_delete_rec_index_and_delay_commit(bukuDb, index, delay_commit, input_retval):\n    \"\"\"test delete rec, index and delay commit.\"\"\"\n    bdb = bukuDb()\n    bdb_dc = bukuDb()  # instance for delay_commit check.\n\n    # Fill bookmark\n    for bookmark in TEST_BOOKMARKS:\n        _add_rec(bdb, *bookmark)\n    db_len = len(TEST_BOOKMARKS)\n\n    n_index = index\n\n    with mock.patch(\"builtins.input\", return_value=input_retval):\n        res = bdb.delete_rec(index=index, delay_commit=delay_commit)\n\n    if n_index < 0:\n        assert not res\n    elif n_index > db_len:\n        assert not res\n        assert len(bdb.get_rec_all()) == db_len\n    elif index == 0 and not input_retval:\n        assert not res\n        assert len(bdb.get_rec_all()) == db_len\n    else:\n        assert res\n        assert len(bdb.get_rec_all()) == db_len - 1\n        if delay_commit:\n            assert len(bdb_dc.get_rec_all()) == db_len\n        else:\n            assert len(bdb_dc.get_rec_all()) == db_len - 1\n\n    # teardown\n    os.environ[\"XDG_DATA_HOME\"] = TEST_TEMP_DIR_PATH\n\n\n@pytest.mark.parametrize(\n    \"index, is_range, low, high\",\n    [\n        # range on non zero index\n        (0, True, 1, 1),\n        # range on zero index\n        (0, True, 0, 0),\n        # zero index only\n        (0, False, 0, 0),\n    ],\n)\ndef test_delete_rec_on_empty_database(bukuDb, index, is_range, low, high):\n    \"\"\"test delete rec, on empty database.\"\"\"\n    bdb = bukuDb()\n    with mock.patch(\"builtins.input\", return_value=\"y\"):\n        res = bdb.delete_rec(index, is_range, low, high)\n\n    if (is_range and any([low == 0, high == 0])) or (not is_range and index == 0):\n        assert res\n        # teardown\n        os.environ[\"XDG_DATA_HOME\"] = TEST_TEMP_DIR_PATH\n        return\n\n    if is_range and low > 1 and high > 1:\n        assert not res\n\n    # teardown\n    os.environ[\"XDG_DATA_HOME\"] = TEST_TEMP_DIR_PATH\n\n\n@pytest.mark.parametrize(\n    \"kwargs, exp_res, raise_error\",\n    [\n        [{\"index\": 'a', \"low\": 'a', \"high\": 1, \"is_range\": True}, None, True],\n        [{\"index\": 'a', \"low\": 'a', \"high\": 1, \"is_range\": False}, None, True],\n        [{\"index\": 'a', \"low\": 1, \"high\": 'a', \"is_range\": True}, None, True],\n        [{\"index\": 'a', \"is_range\": False}, None, True],\n        [{\"index\": 'a', \"is_range\": True}, None, True],\n    ],\n)\ndef test_delete_rec_on_non_integer(\n    bukuDb, tmp_path, monkeypatch, kwargs, exp_res, raise_error\n):\n    \"\"\"test delete rec on non integer arg.\"\"\"\n    import buku\n\n    bdb = bukuDb(dbfile=tmp_path / \"tmp.db\")\n\n    for bookmark in TEST_BOOKMARKS:\n        _add_rec(bdb, *bookmark)\n\n    def mockreturn():\n        return \"y\"\n\n    exp_res = None\n    res = None\n    monkeypatch.setattr(buku, \"read_in\", mockreturn)\n    if raise_error:\n        with pytest.raises(TypeError):\n            res = bdb.delete_rec(**kwargs)\n    else:\n        res = bdb.delete_rec(**kwargs)\n    assert res == exp_res\n\n\n@pytest.mark.parametrize(\"url\", [\"\", False, None, 0])\ndef test_add_rec_add_invalid_url(bukuDb, caplog, url):\n    \"\"\"test method.\"\"\"\n    bdb = bukuDb()\n    res = _add_rec(bdb, url=url)\n    assert res is None\n    caplog.records[0].levelname == \"ERROR\"\n    caplog.records[0].getMessage() == \"Invalid URL\"\n\n\n@pytest.mark.parametrize(\n    \"kwargs, exp_arg\",\n    [\n        [{\"url\": \"example.com\"}, (\"example.com\", \"Example Domain\", \",\", \"\", False)],\n        [\n            {\"url\": \"http://example.com\"},\n            (\"http://example.com\", \"Example Domain\", \",\", \"\", False),\n        ],\n        [\n            {\"url\": \"http://example.com\", \"immutable\": True},\n            (\"http://example.com\", \"Example Domain\", \",\", \"\", True),\n        ],\n        [\n            {\"url\": \"http://example.com\", \"desc\": \"randomdesc\"},\n            (\"http://example.com\", \"Example Domain\", \",\", \"randomdesc\", False),\n        ],\n        [\n            {\"url\": \"http://example.com\", \"title_in\": \"randomtitle\"},\n            (\"http://example.com\", \"randomtitle\", \",\", \"\", False),\n        ],\n        [\n            {\"url\": \"http://example.com\", \"tags_in\": \"tag1\"},\n            (\"http://example.com\", \"Example Domain\", \",tag1,\", \"\", False),\n        ],\n        [\n            {\"url\": \"http://example.com\", \"tags_in\": \",tag1\"},\n            (\"http://example.com\", \"Example Domain\", \",tag1,\", \"\", False),\n        ],\n        [\n            {\"url\": \"http://example.com\", \"tags_in\": \",tag1,\"},\n            (\"http://example.com\", \"Example Domain\", \",tag1,\", \"\", False),\n        ],\n    ],\n)\ndef test_add_rec_exec_arg(bukuDb, kwargs, exp_arg):\n    \"\"\"test func.\"\"\"\n    bdb = bukuDb()\n    _cur = bdb.cur\n    try:\n        bdb.cur = mock.Mock()\n        bdb.get_rec_id = mock.Mock(return_value=None)\n        with mock_fetch(title=exp_arg[1]):\n            bdb.add_rec(**kwargs)\n        assert bdb.cur.execute.call_args[0][1] == exp_arg\n    finally:\n        bdb.cur = _cur\n\n\ndef test_update_rec_index_0(bukuDb, caplog):\n    \"\"\"test method.\"\"\"\n    bdb = bukuDb()\n    res = bdb.update_rec(index=0, url=\"http://example.com\")\n    assert not res\n    assert caplog.records[0].getMessage() == \"All URLs cannot be same\"\n    assert caplog.records[0].levelname == \"ERROR\"\n\n\n@pytest.mark.parametrize(\n    \"kwargs, exp_res\",\n    [\n        [{\"index\": 1}, False],\n        [{\"index\": 1, \"url\": 'url'}, False],\n        [{\"index\": 1, \"url\": ''}, False],\n    ],\n)\ndef test_update_rec(bukuDb, tmp_path, kwargs, exp_res):\n    bdb = bukuDb(tmp_path / \"tmp.db\")\n    res = bdb.update_rec(**kwargs)\n    assert res == exp_res\n\n\n@pytest.mark.parametrize(\"invalid_tag\", [\"+,\", \"-,\"])\ndef test_update_rec_invalid_tag(bukuDb, caplog, invalid_tag):\n    \"\"\"test method.\"\"\"\n    url = \"http://example.com\"\n    bdb = bukuDb()\n    res = bdb.update_rec(index=1, url=url, tags_in=invalid_tag)\n    assert not res\n    assert caplog.records[0].getMessage() == \"Please specify a tag\"\n    assert caplog.records[0].levelname == \"ERROR\"\n\n\n@pytest.mark.parametrize(\n    \"read_in_retval, exp_res, record_tuples\",\n    [\n        [\"y\", False, [(\"root\", 40, \"No matches found\")]],\n        [\"n\", False, []],\n        [\"\", False, []],\n    ],\n)\ndef test_update_rec_update_all_bookmark(\n    caplog, tmp_path, bukuDb, read_in_retval, exp_res, record_tuples\n):\n    \"\"\"test method.\"\"\"\n    with mock.patch(\"buku.read_in\", return_value=read_in_retval):\n        bdb = bukuDb(tmp_path / \"tmp.db\")\n        res = bdb.update_rec(index=0, tags_in=\"tags1\")\n        assert (res, caplog.record_tuples) == (exp_res, record_tuples)\n\n\n@pytest.mark.parametrize(\n    \"get_system_editor_retval, index, exp_res\",\n    [\n        [\"none\", 0, False],\n        [\"nano\", -2, False],\n    ],\n)\ndef test_edit_update_rec_with_invalid_input(bukuDb, get_system_editor_retval, index, exp_res):\n    \"\"\"test method.\"\"\"\n    with mock.patch(\"buku.get_system_editor\", return_value=get_system_editor_retval):\n        assert bukuDb().edit_update_rec(index=index) == exp_res\n\n\n@pytest.mark.vcr(\"tests/vcr_cassettes/test_browse_by_index.yaml\")\n@given(\n    low=st.integers(min_value=-2, max_value=3),\n    high=st.integers(min_value=-2, max_value=3),\n    index=st.integers(min_value=-2, max_value=3),\n    is_range=st.booleans(),\n    empty_database=st.booleans(),\n)\n@example(low=0, high=0, index=0, is_range=False, empty_database=True)\n@settings(max_examples=2, deadline=None)\ndef test_browse_by_index(low, high, index, is_range, empty_database):\n    \"\"\"test method.\"\"\"\n    n_low, n_high = (high, low) if low > high else (low, high)\n    with mock.patch(\"buku.browse\"):\n        import buku\n\n        bdb = buku.BukuDb(TEST_TEMP_DBFILE_PATH)\n        try:\n            bdb.delete_rec_all()\n            db_len = 0\n            if not empty_database:\n                bdb.add_rec(\"https://www.google.com/ncr\", \"?\")\n                db_len += 1\n            res = bdb.browse_by_index(index=index, low=low, high=high, is_range=is_range)\n            if is_range and (low < 0 or high < 0):\n                assert not res\n            elif is_range and n_low > 0 and n_high > 0:\n                assert res\n            elif is_range:\n                assert not res\n            elif not is_range and index < 0:\n                assert not res\n            elif not is_range and index > db_len:\n                assert not res\n            elif not is_range and index >= 0 and empty_database:\n                assert not res\n            elif not is_range and 0 <= index <= db_len and not empty_database:\n                assert res\n            else:\n                raise ValueError\n        finally:\n            rmdb(bdb)\n\n\n@pytest.fixture()\ndef chrome_db():\n    # compatibility\n    dir_path = os.path.dirname(os.path.realpath(__file__))\n    res_yaml_file = os.path.join(dir_path, \"test_bukuDb\", \"25491522_res.yaml\")\n    res_nopt_yaml_file = os.path.join(dir_path, \"test_bukuDb\", \"25491522_res_nopt.yaml\")\n    json_file = os.path.join(dir_path, \"test_bukuDb\", \"Bookmarks\")\n    return json_file, res_yaml_file, res_nopt_yaml_file\n\n\n@pytest.mark.parametrize(\"add_pt\", [True, False])\ndef test_load_chrome_database(bukuDb, chrome_db, add_pt):\n    \"\"\"test method.\"\"\"\n    # compatibility\n    json_file = chrome_db[0]\n    res_yaml_file = chrome_db[1] if add_pt else chrome_db[2]\n    dump_data = False  # NOTE: change this value to dump data\n    if not dump_data:\n        with open(res_yaml_file, \"r\", encoding=\"utf8\", errors=\"surrogateescape\") as f:\n            try:\n                res_yaml = yaml.load(f, Loader=yaml.FullLoader)\n            except RuntimeError:\n                res_yaml = yaml.load(f, Loader=PrettySafeLoader)\n    # init\n    bdb = bukuDb()\n    bdb.add_rec = mock.Mock()\n    bdb.load_chrome_database(json_file, None, add_pt)\n    call_args_list_dict = dict(bdb.add_rec.call_args_list)\n    # test\n    if not dump_data:\n        assert call_args_list_dict == res_yaml\n    # dump data for new test\n    if dump_data:\n        with open(res_yaml_file, \"w\", encoding=\"utf8\", errors=\"surrogateescape\") as f:\n            yaml.dump(call_args_list_dict, f)\n        print(\"call args list dict dumped to:{}\".format(res_yaml_file))\n\n\n@pytest.fixture()\ndef firefox_db(tmpdir):\n    dir_path = os.path.dirname(os.path.realpath(__file__))\n    res_yaml_file = os.path.join(dir_path, \"test_bukuDb\", \"firefox_res.yaml\")\n    res_nopt_yaml_file = os.path.join(dir_path, \"test_bukuDb\", \"firefox_res_nopt.yaml\")\n    ff_db_path = os.path.join(dir_path, \"test_bukuDb\", \"places.sqlite\")\n    if not os.path.isfile(ff_db_path):\n        db = sqlite3.connect(ff_db_path)\n        with open(os.path.join(dir_path, 'test_bukuDb', 'places.sql'), encoding='utf-8') as sql:\n            db.cursor().executescript(sql.read())\n        db.commit()\n    return ff_db_path, res_yaml_file, res_nopt_yaml_file\n\n\n@pytest.mark.parametrize(\"add_pt\", [True, False])\ndef test_load_firefox_database(bukuDb, firefox_db, add_pt):\n    # compatibility\n    ff_db_path = firefox_db[0]\n    dump_data = False  # NOTE: change this value to dump data\n    res_yaml_file = firefox_db[1] if add_pt else firefox_db[2]\n    if not dump_data:\n        with open(res_yaml_file, \"r\", encoding=\"utf8\", errors=\"surrogateescape\") as f:\n            res_yaml = yaml.load(f, Loader=PrettySafeLoader)\n    # init\n    bdb = bukuDb()\n    bdb.add_rec = mock.Mock()\n    bdb.load_firefox_database(ff_db_path, None, add_pt)\n    call_args_list_dict = dict(bdb.add_rec.call_args_list)\n    # test\n    if not dump_data:\n        assert call_args_list_dict == res_yaml\n    if dump_data:\n        with open(res_yaml_file, \"w\", encoding=\"utf8\", errors=\"surrogateescape\") as f:\n            yaml.dump(call_args_list_dict, f)\n        print(\"call args list dict dumped to:{}\".format(res_yaml_file))\n\n\n@pytest.mark.parametrize('ignore_case, fields, expected', [\n    (True, ['+id'],\n     ['http://slashdot.org', 'http://www.zażółćgęśląjaźń.pl/', 'http://example.com/',\n      'javascript:void(0)', 'javascript:void(1)', 'example.com/#']),\n    (True, [],\n     ['http://slashdot.org', 'http://www.zażółćgęśląjaźń.pl/', 'http://example.com/',\n      'javascript:void(0)', 'javascript:void(1)', 'example.com/#']),\n    (True, ['-metadata', '+netloc', '-url', 'id'],\n     ['http://www.zażółćgęśląjaźń.pl/', 'http://example.com/', 'example.com/#',\n      'http://slashdot.org', 'javascript:void(1)', 'javascript:void(0)']),\n    (False, ['-metadata', '+netloc', 'url', 'id'],\n     ['example.com/#', 'http://example.com/', 'javascript:void(0)',\n      'javascript:void(1)', 'http://www.zażółćgęśląjaźń.pl/', 'http://slashdot.org']),\n    (True, ['+title', '-tags', 'description', 'index', 'uri'],\n     ['javascript:void(1)', 'javascript:void(0)', 'http://slashdot.org',\n      'http://example.com/', 'example.com/#', 'http://www.zażółćgęśląjaźń.pl/']),\n    (True, ['# tEst  ', '#invalid,tag', '-index'],\n     ['javascript:void(1)', 'javascript:void(0)', 'http://www.zażółćgęśląjaźń.pl/',\n      'http://slashdot.org', 'example.com/#', 'http://example.com/']),\n    (True, ['-#  teSt ', '-#invalid,tag', '-index'],\n     ['example.com/#', 'http://example.com/', 'javascript:void(1)',\n      'javascript:void(0)', 'http://www.zażółćgęśląjaźń.pl/', 'http://slashdot.org']),\n])\ndef test_sort_and_reorder(bukuDb, fields, ignore_case, expected):\n    _bookmarks = (TEST_BOOKMARKS + [(f'javascript:void({i})', 'foo', parse_tags([f'tag{i}']), 'stuff') for i in range(2)] +\n                  [('example.com/#', 'test', parse_tags(['test,tes,est,es']), 'a case for replace_tag test')])\n    bookmarks = [(i,) + tuple(x) for i, x in enumerate(_bookmarks, start=1)]\n    shuffle(bookmarks)  # making sure sorting by index works as well\n    bdb = bukuDb()\n    assert [x.url for x in bdb._sort(bookmarks, fields, ignore_case=ignore_case)] == expected\n    for bookmark in _bookmarks:\n        _add_rec(bdb, *bookmark)\n    bdb.reorder(fields, ignore_case=ignore_case)\n    assert [x.url for x in bdb.get_rec_all()] == expected\n\n@pytest.mark.parametrize('ignore_case, fields, expected', [\n    (True, ['+id'], 'id ASC'),\n    (True, [], 'id ASC'),\n    (False, ['-metadata', '+netloc', 'url', 'id'], 'metadata DESC, LOWER(NETLOC(url)) ASC, url ASC, id ASC'),\n    (True, ['-metadata', '+netloc', '-url', 'id'], 'LOWER(metadata) DESC, LOWER(NETLOC(url)) ASC, LOWER(url) DESC, id ASC'),\n    (False, ['+title', '-tags', 'description', 'index', 'uri'], 'metadata ASC, tags DESC, desc ASC, id ASC, url ASC'),\n    (True, ['+title', '-tags', 'description', 'index', 'uri'],\n     'LOWER(metadata) ASC, LOWER(tags) DESC, LOWER(desc) ASC, id ASC, LOWER(url) ASC'),\n    (True, ['#foo', '# BaR ', \"-# b'A'z \", '# invalid, tag '],\n     \"tags LIKE '%,foo,%' ASC, tags LIKE '%,bar,%' ASC, tags LIKE '%,b''a''z,%' DESC\")\n])\ndef test_order(bukuDb, fields, ignore_case, expected):\n    assert bukuDb()._order(fields, ignore_case=ignore_case) == expected\n\n@pytest.mark.parametrize('order, expected', [\n    (['netloc'], ['http://example.com/', 'https://example.com', '//example.com#',\n                  'example.com?', 'http://slashdot.org', 'http://www.zażółćgęśląjaźń.pl/']),\n    (['-netloc'], ['http://www.zażółćgęśląjaźń.pl/', 'http://slashdot.org', 'http://example.com/',\n                   'https://example.com', '//example.com#', 'example.com?']),\n    (['netloc', 'url'], ['//example.com#', 'example.com?', 'http://example.com/',\n                         'https://example.com', 'http://slashdot.org', 'http://www.zażółćgęśląjaźń.pl/']),\n    (['netloc', '-url'], ['https://example.com', 'http://example.com/', 'example.com?',\n                          '//example.com#', 'http://slashdot.org', 'http://www.zażółćgęśląjaźń.pl/']),\n])\ndef test_order_by_netloc(bukuDb, order, expected):\n    bdb = bukuDb()\n    _EXTRA = ['https://example.com', '//example.com#', 'example.com?']\n    for bookmark in (TEST_BOOKMARKS + [(url, 'test', parse_tags(['test,tes,est,es']), 'a case for replace_tag test') for url in _EXTRA]):\n        _add_rec(bdb, *bookmark)\n    assert [x.url for x in bdb.get_rec_all(order=order)] == expected\n\n@pytest.mark.parametrize('keyword, params, expected', [\n    ('', {}, []),\n    ('', {'markers': True}, []),\n    ('*', {'markers': True}, []),\n    (':', {'markers': True}, []),\n    ('>', {'markers': True}, []),\n    ('#', {'markers': True}, []),\n    ('#,', {'markers': True}, []),\n    ('# ,, ,', {'markers': True}, []),\n    ('#, ,, ,', {'markers': True}, []),\n    ('foo, bar?, , baz', {'regex': True}, [\n        ('metadata', False, 'foo, bar?, , baz'), ('url', False, 'foo, bar?, , baz'),\n        ('desc', False, 'foo, bar?, , baz'), ('tags', False, 'foo, bar?, , baz'),\n    ]),\n    ('foo, bar?, , baz', {}, [\n        ('metadata', False, 'foo, bar?, , baz'), ('url', False, 'foo, bar?, , baz'),\n        ('desc', False, 'foo, bar?, , baz'), ('tags', False, 'bar?', 'baz', 'foo'),\n    ]),\n    ('foo, bar?, , baz', {'deep': True}, [\n        ('metadata', True, 'foo, bar?, , baz'), ('url', True, 'foo, bar?, , baz'),\n        ('desc', True, 'foo, bar?, , baz'), ('tags', True, 'bar?', 'baz', 'foo'),\n    ]),\n    ('foo, bar?, , baz', {'markers': True}, [\n        ('metadata', False, 'foo, bar?, , baz'), ('url', False, 'foo, bar?, , baz'),\n        ('desc', False, 'foo, bar?, , baz'), ('tags', False, 'bar?', 'baz', 'foo'),\n    ]),\n    ('foo, bar?, , baz', {'deep': True, 'markers': True}, [\n        ('metadata', True, 'foo, bar?, , baz'), ('url', True, 'foo, bar?, , baz'),\n        ('desc', True, 'foo, bar?, , baz'), ('tags', True, 'bar?', 'baz', 'foo'),\n    ]),\n    ('*foo, bar?, , baz', {'markers': True}, [\n        ('metadata', False, 'foo, bar?, , baz'), ('url', False, 'foo, bar?, , baz'),\n        ('desc', False, 'foo, bar?, , baz'), ('tags', False, 'bar?', 'baz', 'foo'),\n    ]),\n    ('*foo, bar?, , baz', {'deep': True, 'markers': True}, [\n        ('metadata', True, 'foo, bar?, , baz'), ('url', True, 'foo, bar?, , baz'),\n        ('desc', True, 'foo, bar?, , baz'), ('tags', True, 'bar?', 'baz', 'foo'),\n    ]),\n    ('.foo, bar?, , baz', {'markers': True}, [('metadata', False, 'foo, bar?, , baz')]),\n    ('.foo, bar?, , baz', {'deep': True, 'markers': True}, [('metadata', True, 'foo, bar?, , baz')]),\n    (':foo, bar?, , baz', {'markers': True}, [('url', False, 'foo, bar?, , baz')]),\n    (':foo, bar?, , baz', {'deep': True, 'markers': True}, [('url', True, 'foo, bar?, , baz')]),\n    ('>foo, bar?, , baz', {'markers': True}, [('desc', False, 'foo, bar?, , baz')]),\n    ('>foo, bar?, , baz', {'deep': True, 'markers': True}, [('desc', True, 'foo, bar?, , baz')]),\n    ('#foo, bar?, , baz', {'markers': True}, [('tags', True, 'bar?', 'baz', 'foo')]),\n    ('#foo, bar?, , baz', {'deep': True, 'markers': True}, [('tags', True, 'bar?', 'baz', 'foo')]),\n    ('#foo, bar?, , baz', {'regex': True, 'markers': True}, [('tags', True, 'foo, bar?, , baz')]),\n    ('#,foo, bar?, , baz', {'markers': True}, [('tags', False, 'bar?', 'baz', 'foo')]),\n    ('#,foo, bar?, , baz', {'deep': True, 'markers': True}, [('tags', False, 'bar?', 'baz', 'foo')]),\n    ('#,foo, bar?, , baz', {'regex': True, 'markers': True}, [('tags', False, 'foo, bar?, , baz')]),\n])\ndef test_search_tokens(bukuDb, keyword, params, expected):\n    assert bukuDb()._search_tokens(keyword, **params) == expected\n\n@pytest.mark.parametrize('regex, tokens, args, clauses', [\n    (True, [], [], ''),\n    (True, [('metadata', False, 'foo, bar?, , baz')], [r'foo, bar?, , baz'], 'metadata REGEXP ?'),   # escape manually\n    (True, [('tags', False, 'foo, bar?, , baz')], [r'foo, bar?, , baz'], 'tags REGEXP ?'),  # specify borders manually\n    (True, [('metadata', False, 'foo, bar?, , baz'), ('url', False, 'foo, bar?, , baz'),\n            ('desc', False, 'foo, bar?, , baz'), ('tags', False, 'foo, bar?, , baz')],\n     [r'foo, bar?, , baz']*4, 'metadata REGEXP ? OR url REGEXP ? OR desc REGEXP ? OR tags REGEXP ?'),\n    (False, [], [], ''),\n    (False, [('desc', False, 'foo, bar?, , baz')], [r'\\bfoo,\\ bar\\?,\\ ,\\ baz\\b'], 'desc REGEXP ?'),\n    (False, [('desc', True, 'foo, bar?, , baz')], ['foo, bar?, , baz'], \"desc LIKE ('%' || ? || '%')\"),\n    (False, [('tags', False, 'bar?', 'baz', 'foo')], [r',bar\\?,', r',baz,', r',foo,'],\n     '(tags REGEXP ? AND tags REGEXP ? AND tags REGEXP ?)'),\n    (False, [('tags', True, 'bar?', 'baz', 'foo')], ['bar?', 'baz', 'foo'],\n     \"(tags LIKE ('%' || ? || '%') AND tags LIKE ('%' || ? || '%') AND tags LIKE ('%' || ? || '%'))\"),\n    (False, [('metadata', False, 'foo, bar?, , baz'), ('url', False, 'foo, bar?, , baz'),\n             ('desc', False, 'foo, bar?, , baz'), ('tags', False, 'bar?', 'baz', 'foo')],\n     [r'\\bfoo,\\ bar\\?,\\ ,\\ baz\\b']*3 + [r',bar\\?,', r',baz,', r',foo,'],\n     'metadata REGEXP ? OR url REGEXP ? OR desc REGEXP ? OR (tags REGEXP ? AND tags REGEXP ? AND tags REGEXP ?)'),\n    (False, [('metadata', True, 'foo, bar?, , baz'), ('url', True, 'foo, bar?, , baz'),\n             ('desc', True, 'foo, bar?, , baz'), ('tags', True, 'bar?', 'baz', 'foo')],\n     ['foo, bar?, , baz']*3 + ['bar?', 'baz', 'foo'],\n     \"metadata LIKE ('%' || ? || '%') OR url LIKE ('%' || ? || '%') OR desc LIKE ('%' || ? || '%')\"\n     \" OR (tags LIKE ('%' || ? || '%') AND tags LIKE ('%' || ? || '%') AND tags LIKE ('%' || ? || '%'))\"),\n])\ndef test_search_clause(bukuDb, regex, tokens, args, clauses):\n    assert bukuDb()._search_clause(tokens, regex=regex) == (clauses, args)\n\n@pytest.mark.parametrize('keywords, params, expected', [\n    (['slashdot'], {}, ['http://slashdot.org']),\n    (['slashdot|example'], {'regex': True}, ['http://slashdot.org', 'http://example.com/']),\n    (['slashdot|example'], {'regex': True, 'order': ['-title']}, ['http://example.com/', 'http://slashdot.org']),\n    (['old,news,old'], {}, ['http://slashdot.org']),  # tags matching\n    (['bold,news,old'], {}, []),  # ALL tags within a token must match\n    (['#test'], {'markers': True}, ['http://example.com/']),\n    (['#es,test'], {'markers': True}, ['http://example.com/']),\n    (['#te'], {'markers': True}, ['http://example.com/']),\n    (['#,te'], {'markers': True}, []),\n    (['#,es'], {'markers': True}, ['http://example.com/']),\n    (['#,es,te'], {'markers': True}, []),  # ALL tags within a token must match\n    (['>for', ':com'], {'markers': True, 'all_keywords': True}, ['http://example.com/']),\n    (['>for', ':com'], {'markers': True, 'all_keywords': False}, ['http://example.com/', 'http://slashdot.org']),\n    (['>test'], {'markers': True, 'deep': False}, ['http://example.com/']),\n    (['>test'], {'markers': True, 'deep': True, 'order': ['title']}, ['http://example.com/', 'http://www.zażółćgęśląjaźń.pl/']),\n])\ndef test_searchdb(bukuDb, keywords, params, expected):\n    bdb = bukuDb()\n    for bookmark in TEST_BOOKMARKS:\n        _add_rec(bdb, *bookmark)\n    assert [x.url for x in bdb.searchdb(keywords, **params)] == expected\n\n\n@pytest.mark.parametrize('keyword_results, stag_results, exp_res', [\n    ([], [], []),\n    ([\"item1\"], [\"item1\", \"item2\"], [\"item1\"]),\n    ([\"item2\"], [\"item1\"], []),\n])\ndef test_search_keywords_and_filter_by_tags(bukuDb, keyword_results, stag_results, exp_res):\n    with mock.patch('buku.BukuDb.searchdb', return_value=keyword_results):\n        with mock.patch('buku.BukuDb.search_by_tag', return_value=stag_results):\n            assert exp_res == bukuDb().search_keywords_and_filter_by_tags(['keywords'], stag=['stag'])\n\n\n@pytest.mark.parametrize('search_results, exclude_results, exp_res', [\n    ([], [], []),\n    ([\"item1\", \"item2\"], [\"item2\"], [\"item1\"]),\n    ([\"item2\"], [\"item1\"], [\"item2\"]),\n    ([\"item1\", \"item2\"], [\"item1\", \"item2\"], []),\n])\ndef test_exclude_results_from_search(bukuDb, search_results, exclude_results, exp_res):\n    with mock.patch('buku.BukuDb.searchdb', return_value=exclude_results):\n        assert exp_res == bukuDb().exclude_results_from_search(search_results, ['without'])\n\n\ndef test_exportdb_empty_db(bukuDb):\n    with NamedTemporaryFile(delete=False) as f:\n        db = bukuDb(dbfile=f.name)\n        with NamedTemporaryFile(delete=False) as f2:\n            res = db.exportdb(f2.name)\n            assert not res\n\n\ndef test_exportdb_single_rec(bukuDb, tmpdir):\n    f1 = NamedTemporaryFile(delete=False)\n    f1.close()\n    db = bukuDb(dbfile=f1.name)\n    _add_rec(db, \"http://example.com\")\n    exp_file = tmpdir.join(\"export\")\n    db.exportdb(exp_file.strpath)\n    with open(exp_file.strpath, encoding=\"utf8\", errors=\"surrogateescape\") as f2:\n        assert f2.read()\n\n\ndef test_exportdb_to_db(bukuDb):\n    f1 = NamedTemporaryFile(delete=False)\n    f1.close()\n    f2 = NamedTemporaryFile(delete=False, suffix=\".db\")\n    f2.close()\n    db = bukuDb(dbfile=f1.name)\n    _add_rec(db, \"http://example.com\")\n    _add_rec(db, \"http://google.com\")\n    with mock.patch(\"builtins.input\", return_value=\"y\"):\n        db.exportdb(f2.name)\n    db2 = bukuDb(dbfile=f2.name)\n    assert db.get_rec_all() == db2.get_rec_all()\n\n\n@pytest.mark.parametrize('pick', [None, 0, 3, 7, 10])\n@mock.patch('builtins.print')\n@mock.patch('builtins.open')\n@mock.patch('random.sample')\n@mock.patch('buku.convert_bookmark_set')\n@mock.patch('buku.BukuDb._sort')\ndef test_exportdb_pick(_bukudb_sort, _convert_bookmark_set, _sample, _open, _print, bukuDb, pick):\n    wrap = mock.Mock()\n    wrap.attach_mock(_print, 'print')\n    wrap.attach_mock(_open, 'open')\n    wrap.attach_mock(_sample, 'sample')\n    wrap.attach_mock(_convert_bookmark_set, 'convert_bookmark_set')\n    wrap.attach_mock(_bukudb_sort, 'BukuDb_sort')\n    _sample.return_value = _sampled = object()\n    _bukudb_sort.return_value = _selection = object()\n    _convert_bookmark_set.return_value = _converted = {'data': object(), 'count': 42}\n    filepath, order, records, picked = 'output.md', object(), range(7), pick and pick < 7\n\n    bdb = bukuDb()\n    assert bdb.exportdb(filepath, records, order=order, pick=pick)\n    pick_expected = [mock.call.sample(records, pick),\n                     mock.call.BukuDb_sort(_sampled, order)]\n    expected_calls = [mock.call.open(filepath, mode='w', encoding='utf-8'),\n                      mock.call.open().__enter__(),                            # pylint: disable=unnecessary-dunder-call\n                      mock.call.convert_bookmark_set((_selection if picked else records), 'markdown', {}),\n                      mock.call.open().__enter__().write(_converted['data']),  # pylint: disable=unnecessary-dunder-call\n                      mock.call.print('42 exported'),\n                      mock.call.open().__exit__(None, None, None)]\n    assert wrap.mock_calls == ([] if not picked else pick_expected) + expected_calls\n\n\n@pytest.mark.parametrize(\n    \"urls, exp_res\",\n    [\n        [[], None],\n        [[\"http://example.com\"], 1],\n        [[\"http://example.com\", \"http://google.com\"], 2],\n    ],\n)\ndef test_get_max_id(bukuDb, urls, exp_res):\n    with NamedTemporaryFile(delete=False) as f:\n        db = bukuDb(dbfile=f.name)\n        if urls:\n            list(map(lambda x: _add_rec(db, x), urls))\n        assert db.get_max_id() == exp_res\n\n\n# Helper functions for testcases\n\n\ndef split_and_test_membership(a, b):\n    # :param a, b: comma separated strings to split\n    # test everything in a in b\n    return all(x in b.split(\",\") for x in a.split(\",\"))\n\n\ndef inclusive_range(start, end):\n    return list(range(start, end + 1))\n\n\ndef normalize_range(db_len, low, high):\n    \"\"\"normalize index and range.\n\n    Args:\n        db_len (int): database length.\n        low (int): low limit.\n        high (int): high limit.\n\n    Returns:\n        Tuple contain following normalized variables (low, high)\n    \"\"\"\n    require_comparison = True\n    # don't deal with non instance of the variable.\n    if not isinstance(low, int):\n        n_low = low\n        require_comparison = False\n    if not isinstance(high, int):\n        n_high = high\n        require_comparison = False\n\n    max_value = db_len\n    if low == \"max\" and high == \"max\":\n        n_low = db_len\n        n_high = max_value\n    elif low == \"max\" and high != \"max\":\n        n_low = high\n        n_high = max_value\n    elif low != \"max\" and high == \"max\":\n        n_low = low\n        n_high = max_value\n    else:\n        n_low = low\n        n_high = high\n\n    if require_comparison:\n        if n_high < n_low:\n            n_high, n_low = n_low, n_high\n\n    return (n_low, n_high)\n\n\nif __name__ == \"__main__\":\n    unittest.main()\n"
  },
  {
    "path": "tests/test_cli.py",
    "content": "from unittest import mock\nfrom io import StringIO\nimport os\nimport pytest\n\nimport buku\n\n\n@pytest.fixture\ndef stdin(monkeypatch):\n    with monkeypatch.context():\n        monkeypatch.setattr('sys.stdin', (buffer := StringIO()))\n        yield buffer\n\n@pytest.fixture\ndef BukuDb():\n    with mock.patch('buku.BukuDb') as cls:\n        cls.return_value.close_quit.side_effect = SystemExit\n        yield cls\n\n@pytest.fixture\ndef bdb(BukuDb):\n    yield BukuDb.return_value\n\n@pytest.fixture\ndef piped_input():\n    with mock.patch('buku.piped_input') as fn:\n        yield fn\n\n@pytest.fixture\ndef prompt():\n    with mock.patch('buku.prompt') as fn:\n        yield fn\n\n@pytest.fixture\ndef exit():\n    with mock.patch('sys.exit', side_effect=SystemExit) as fn:\n        yield fn\n\n\ndef test_version(BukuDb, piped_input, capsys):\n    with pytest.raises(SystemExit):\n        buku.main(['--version'])\n    assert capsys.readouterr().out.splitlines() == [buku.__version__]\n\ndef test_usage(BukuDb, piped_input, monkeypatch, capsys):\n    with pytest.raises(SystemExit):\n        buku.main(['--unknown'], program_name='buku')\n    BukuDb.assert_not_called()\n    assert capsys.readouterr().err.splitlines() == [\n        'usage: buku [OPTIONS] [KEYWORD [KEYWORD ...]]',\n        'buku: error: unrecognized arguments: --unknown',\n    ]\n\n@pytest.mark.parametrize('argv', [['--help'], ['foo', 'bar', '--help']])\ndef test_help(BukuDb, exit, piped_input, argv):\n    with mock.patch('buku.ExtendedArgumentParser.print_help') as print_help:\n        with pytest.raises(SystemExit):\n            buku.main(argv)\n    BukuDb.assert_not_called()\n    print_help.assert_called_with()\n    exit.assert_called_with(0)\n\n@pytest.mark.parametrize('nostdin', [True, False])\n@pytest.mark.parametrize('db', [None, './foo.db'])\ndef test_prompt(BukuDb, bdb, piped_input, prompt, nostdin, db):\n    argv = (['--nostdin'] if nostdin else []) + (['--db', db] if db else [])\n    BukuDb.get_default_dbdir.return_value = '/default/db/dir'\n    with pytest.raises(SystemExit):\n        buku.main(argv)\n    if argv and argv[0] != '--nostdin':\n        piped_input.assert_called_with(argv, [])\n    else:\n        piped_input.assert_not_called()\n    BukuDb.assert_called_with(dbfile=db or os.path.join('/default/db/dir', 'bookmarks.db'), default_scheme=buku.SCHEME_HTTP)\n    prompt.assert_called_with(bdb, None)\n    bdb.close_quit.assert_called_with(0)\n\n\n@pytest.mark.parametrize('fetch_params', [\n    {'offline': True},\n    {'url_redirect': True},\n    {'tag_redirect': True, 'tag_error': True},\n    {'url_redirect': True, 'tag_redirect': 'redirect', 'tag_error': 'error'},\n    {'url_redirect': True, 'tag_redirect': 'redirect', 'del_range': [], 'del_error': range(400, 600)},\n    {'url_redirect': True, 'tag_redirect': 'redirect', 'tag_error': 'error',\n     'del_range': ['400-404', '500'], 'del_error': {400, 401, 402, 403, 404, 500}},\n])\n@pytest.mark.parametrize('value_params', [\n    {'add_tags': ['foo,bar', 'baz'], 'tags_fetch': False, 'tags_in': ',bar baz,foo,', 'title': ''},\n    {'tag': ['foo', 'bar,baz'], 'tags_fetch': False, 'tags_in': ',baz,foo bar,', 'title': 'Custom Title'},\n    {'add_tags': ['+', 'foo', 'bar', 'baz'], 'tags_in': ',foo bar baz,', 'comment': ''},\n    {'tag': ['+', 'foo,bar,baz'], 'tags_in': ',bar,baz,foo,', 'comment': 'Custom Description'},\n    {'add_tags': ['-', 'foo', 'baz', 'baz'], 'tags_except': ',foo baz baz,', 'immutable': False},\n    {'tag': ['-', 'foo,', ',baz,', ',baz'], 'tags_except': ',baz,foo,', 'immutable': True},\n    {'add_tags': ['foo,baz,bar'], 'tag': ['baz,qux'],\n     'tags_fetch': False, 'tags_in': ',bar,baz,foo,qux,'},\n    {'add_tags': ['+', 'foo,baz,bar'], 'tag': ['baz,', 'qux,'],\n     'tags_fetch': False, 'tags_in': ',bar,baz,foo,qux,'},\n    {'add_tags': ['foo,baz,', 'bar,'], 'tag': ['+', 'baz,qux'],\n     'tags_fetch': False, 'tags_in': ',bar,baz,foo,qux,'},\n    {'add_tags': ['-', 'foo,baz,bar,'], 'tag': ['baz,', 'qux,'],\n     'tags_fetch': False, 'tags_in': ',baz,qux,', 'tags_except': ',bar,baz,foo,'},\n    {'add_tags': ['foo,baz,', 'bar,'], 'tag': ['-', 'baz,qux'],\n     'tags_fetch': False, 'tags_in': ',bar,baz,foo,', 'tags_except': ',baz,qux,'},\n    {'add_tags': ['-', 'foo,baz,bar,'], 'tag': ['-', 'baz,', 'qux,'], 'tags_except': ',bar,baz,foo,qux,'},\n    {'add_tags': ['-', 'foo,baz,', 'bar,'], 'tag': ['+', 'baz,', 'qux,'],\n     'tags_in': ',baz,qux,', 'tags_except': ',bar,baz,foo,'},\n    {'add_tags': ['+', 'foo,baz,', 'bar,'], 'tag': ['-', 'baz,', 'qux,'],\n     'tags_in': ',bar,baz,foo,', 'tags_except': ',baz,qux,'},\n])\ndef test_add(stdin, bdb, prompt, value_params, fetch_params):\n    _test_add(bdb, prompt, **value_params, **fetch_params)\n\ndef _test_add(bdb, prompt, *, add_tags=[], tag=[], tags_fetch=True, tags_in=None, tags_except=None,\n              title=None, comment=None, immutable=None, offline=False, url_redirect=False,\n              tag_redirect=False, tag_error=False, del_range=None, del_error=None):\n    argv = ['--add', (url := 'https://example.com/')] + add_tags\n    if tag:\n        argv += ['--tag'] + tag\n    if title is not None:\n        argv += ['--title', title]\n    if comment is not None:\n        argv += ['--comment', comment]\n    if immutable is not None:\n        argv += ['--immutable', str(int(immutable))]\n    if offline:\n        argv += ['--offline']\n    if url_redirect:\n        argv += ['--url-redirect']\n    if tag_redirect:\n        argv += ['--tag-redirect'] + ([] if isinstance(tag_redirect, bool) else [tag_redirect])\n    if tag_error:\n        argv += ['--tag-error'] + ([] if isinstance(tag_error, bool) else [tag_error])\n    if del_error:\n        argv += ['--del-error'] + del_range\n    print(argv)\n    with pytest.raises(SystemExit):\n        buku.main(argv)\n    network_test = url_redirect or tag_redirect or tag_error or del_error\n    fetch = not offline and (network_test or tags_fetch or title is None)\n    bdb.add_rec.assert_called_with(\n        url, title, tags_in, comment, immutable, delay_commit=False, fetch=fetch,\n        tags_fetch=tags_fetch, tags_except=tags_except, url_redirect=url_redirect,\n        tag_redirect=tag_redirect, tag_error=tag_error, del_error=del_error)\n    bdb.searchdb.assert_not_called()\n    prompt.assert_not_called()\n    bdb.close_quit.assert_called_with(0)\n\n\n@pytest.mark.parametrize('np', [{}, {'np': []}])\n@pytest.mark.parametrize('count', [{}, {'count': ['10']}])\n@pytest.mark.parametrize('order, indices, command', [\n    (['tags', '-netloc', '+url'], None, {'order': ['tags,-netloc,+url'], 'print': []}),\n    (['-description', '+uri'], [5, 8, 9, 10, 11, 12, 40, 41, 42],\n     {'order': [',-description', '+uri'], 'print': ['5', '8-12', '-3']}),\n])\ndef test_order_print(bdb, stdin, prompt, order, indices, command, count, np):\n    command = dict(command, **count, **np)\n    argv = [s for k, v in command.items() for s in ([f'--{k}'] + v)]\n    print(argv)\n    result = [None] * 20\n    bdb.list_using_id.return_value = result\n    bdb.get_max_id.return_value = 42\n    with pytest.raises(SystemExit):\n        buku.main(argv)\n    bdb.get_max_id.assert_called_with()\n    if not (_count := command.get('count')):\n        bdb.print_rec.assert_called_with(indices, order=order)\n    else:\n        if not command['print']:\n            bdb.list_using_id.assert_called_with(order=order)\n        else:\n            bdb.list_using_id.assert_called_with(command['print'], order=order)\n        prompt.assert_called_with(bdb, result, noninteractive=('np' in command), num=int(_count[0]), order=order)\n\n@pytest.mark.parametrize('search', ['', 'sany', 'sall', 'sreg', 'stag'])\n@pytest.mark.parametrize('exclude', [None, ['xyzzy', 'grue']])\n@pytest.mark.parametrize('keywords, rest', [\n    ([], {}),\n    (['foo', 'bar'], {'markers': []}),\n    (['foo', 'bar'], {'deep': []}),\n    (['foo', 'bar'], {'stag': ['baz', 'qux']})\n])\ndef test_order_search(bdb, stdin, prompt, search, exclude, keywords, rest):\n    if (search == '' and not keywords) or (search == 'stag' and 'stag' in rest):\n        pytest.skip('Invalid combination')\n    order, stag, deep, markers = ['title', '-index'], rest.get('stag'), 'deep' in rest, 'markers' in rest\n    argv = ([] if search == '' else [f'--{search}']) + keywords + ['--order', ','.join(order)]\n    argv += [s for k, v in rest.items() for s in ([f'--{k}'] + v)] + ([] if not exclude else ['--exclude'] + exclude)\n    bdb.search_by_tag.return_value = ['tag search results']\n    with pytest.raises(SystemExit):\n        buku.main(argv)\n    if search == 'stag':\n        if not keywords:\n            prompt.assert_called_with(bdb, None, noninteractive=False, listtags=True, suggest=False, order=order)\n        else:\n            bdb.search_by_tag.assert_called_with(' '.join(keywords), order=order)\n            bdb.exclude_results_from_search.assert_called_with(\n                bdb.search_by_tag.return_value, exclude, deep=deep, markers=markers)\n    if search == 'stag' or not keywords:\n        bdb.search_keywords_and_filter_by_tags.assert_not_called()\n    elif search in ('', 'sany'):\n        bdb.search_keywords_and_filter_by_tags.assert_called_with(\n            keywords, deep=deep, stag=stag, markers=markers, without=exclude, order=order)\n    elif search == 'sall':\n        bdb.search_keywords_and_filter_by_tags.assert_called_with(\n            keywords, all_keywords=True, deep=deep, stag=stag, markers=markers, without=exclude, order=order)\n    elif search == 'sreg':\n        bdb.search_keywords_and_filter_by_tags.assert_called_with(\n            keywords, regex=True, stag=stag, markers=markers, without=exclude, order=order)\n\n@pytest.mark.parametrize('json', [None, '', 'output.json'])\n@pytest.mark.parametrize('indices', [None, '', '1-10', '-10'])  # None = search\n@pytest.mark.parametrize('random', [None, 1, 3])\n@mock.patch('random.sample', return_value='sampled')\n@mock.patch('buku.print_rec_with_filter')\n@mock.patch('buku.write_string_to_file')\n@mock.patch('buku.format_json', return_value='formatted')\n@mock.patch('buku.print_json_safe')\ndef test_random(_print_json_safe, _format_json, _write_string_to_file, _print_rec_with_filter, _sample,\n                bdb, stdin, prompt, random, indices, json):\n    wrap = mock.Mock()\n    wrap.attach_mock(_sample, 'random_sample')\n    wrap.attach_mock(_print_rec_with_filter, 'print_rec_with_filter')\n    wrap.attach_mock(_write_string_to_file, 'write_string_to_file')\n    wrap.attach_mock(_format_json, 'format_json')\n    wrap.attach_mock(_print_json_safe, 'print_json_safe')\n    wrap.attach_mock(prompt, 'prompt')\n    wrap.attach_mock(bdb, 'bdb')\n    bdb.get_max_id.return_value = 42\n    bdb._sort.return_value = 'sorted'\n    bdb.search_keywords_and_filter_by_tags.return_value = 'found'\n    argv = (['--sall', 'foo'] if indices is None else ['--print'] + ([] if not indices else [indices]))\n    argv += ([] if json is None else ['--json'] + ([] if not json else [json]))\n    argv += ([] if not random else ['--random'] + ([] if random == 1 else [str(random)]))\n    with pytest.raises(SystemExit):\n        buku.main(argv)\n    calls = ([] if indices is None else [mock.call.bdb.get_max_id()])\n    if indices:                # --print 1-10\n        idxs = list(range(33, 43) if indices == '-10' else range(1, 11))\n        calls += ([mock.call.bdb.print_rec(idxs, order=[])] if not random else\n                  [mock.call.random_sample(idxs, random),\n                   mock.call.bdb.print_rec('sampled', order=[])])\n    elif indices is not None:  # --print\n        calls += ([mock.call.bdb.print_rec(None, order=[])] if not random else\n                  [mock.call.random_sample(range(1, 43), random),\n                   mock.call.bdb.print_rec('sampled', order=[])])\n    else:                      # --sall foo\n        calls += [mock.call.bdb.search_keywords_and_filter_by_tags(\n                      ['foo'], all_keywords=True, deep=False, stag=None, markers=False, without=None, order=[])]\n        if random:\n            calls += [mock.call.random_sample('found', random),\n                      mock.call.bdb._sort('sampled', [])]\n        res = ('sorted' if random else 'found')\n        if json:\n            calls += [mock.call.format_json(res, (random == 1), field_filter=0),\n                      mock.call.write_string_to_file('formatted', json)]\n        elif json is not None:\n            calls += [mock.call.print_json_safe(res, (random == 1), field_filter=0)]\n        elif random:\n            calls += [mock.call.print_rec_with_filter(res, field_filter=0)]\n        else:\n            calls += [mock.call.prompt(bdb, res, noninteractive=False, deep=False, markers=False, order=[], num=10)]\n    calls += [mock.call.bdb.close_quit(0)]\n    assert wrap.mock_calls == calls\n\n@pytest.mark.parametrize('search', [True, False])\n@pytest.mark.parametrize('random', [None, 1, 3])\n@mock.patch('random.sample', return_value='sampled')\n@mock.patch('buku.print_rec_with_filter')\ndef test_random_export(_print_rec_with_filter, _sample, bdb, stdin, prompt, random, search):\n    wrap = mock.Mock()\n    wrap.attach_mock(_sample, 'random_sample')\n    wrap.attach_mock(_print_rec_with_filter, 'print_rec_with_filter')\n    wrap.attach_mock(prompt, 'prompt')\n    wrap.attach_mock(bdb, 'bdb')\n    bdb.get_max_id.return_value = 42\n    bdb._sort.return_value = 'sorted'\n    bdb.search_keywords_and_filter_by_tags.return_value = 'found'\n    argv = ['--export', 'export.md'] + ([] if not search else ['--sall', 'foo'])\n    argv += ([] if not random else ['--random'] + ([] if random == 1 else [str(random)]))\n    with pytest.raises(SystemExit):\n        buku.main(argv)\n    calls = []\n    if not search:\n        calls += [mock.call.bdb.exportdb('export.md', order=[], pick=random)]\n    else:\n        calls += [mock.call.bdb.search_keywords_and_filter_by_tags(\n                      ['foo'], all_keywords=True, deep=False, stag=None, markers=False, without=None, order=[])]\n        if random:\n            calls += [mock.call.random_sample('found', random),\n                      mock.call.bdb._sort('sampled', [])]\n        res = ('sorted' if random else 'found')\n        if random:\n            calls += [mock.call.print_rec_with_filter(res, field_filter=0)]\n        else:\n            calls += [mock.call.prompt(bdb, res, noninteractive=True, deep=False, markers=False, order=[], num=10)]\n        calls += [mock.call.bdb.exportdb('export.md', res)]\n    calls += [mock.call.bdb.close_quit(0)]\n    assert wrap.mock_calls == calls\n\n\n@pytest.mark.parametrize('db', [None, './foo.db', 'bar.sqlite', 'name'])\n@pytest.mark.parametrize('action', ['print', 'lock', 'unlock'])\n@mock.patch('buku.BukuCrypt')\ndef test_custom_db(_BukuCrypt, BukuDb, stdin, db, action):\n    wrap = mock.Mock()\n    wrap.attach_mock(BukuDb, 'BukuDb')\n    wrap.attach_mock(BukuDb.return_value, 'bdb')\n    wrap.attach_mock(_BukuCrypt, 'BukuCrypt')\n    BukuDb.return_value.get_max_id.return_value = None\n    BukuDb.get_default_dbdir.return_value = '/default/db/dir'\n    _db = (db if db != 'name' else '/default/db/dir/name.db')\n    argv = ['--nostdin'] + ([] if not db else ['--db', db]) + [f'--{action}']\n    with pytest.raises(SystemExit):\n        buku.main(argv)\n    calls = []\n    if db == 'name':\n        calls += [mock.call.BukuDb.get_default_dbdir()]\n    if action == 'lock':\n        calls += [mock.call.BukuCrypt.encrypt_file(8, dbfile=_db)]\n    else:\n        if action == 'unlock':\n            calls += [mock.call.BukuCrypt.decrypt_file(8, dbfile=_db)]\n        calls += [mock.call.BukuDb(None, 0, True, dbfile=_db, colorize=True, default_scheme=buku.SCHEME_HTTP)]\n        if action == 'print':\n            calls += [mock.call.bdb.get_max_id(),\n                      mock.call.bdb.print_rec(None, order=[])]\n        calls += [mock.call.bdb.close_quit(0)]\n    assert wrap.mock_calls == calls\n"
  },
  {
    "path": "tests/test_import_firefox_json.py",
    "content": "import json\nfrom buku import import_firefox_json\n\n\ndef test_load_from_empty():\n    \"\"\"test method.\"\"\"\n    # Arrange\n    data = json.loads(\"{}\")\n\n    # Act\n    items = import_firefox_json(data)\n\n    # Assert\n    count = sum(1 for _ in items)\n    assert count == 0\n\ndef test_load_full_entry():\n    \"\"\"test method.\"\"\"\n\n    # Arrange\n    data = json.loads(\"\"\"\n        {\n            \"title\" : \"main\",\n            \"typeCode\": 2,\n            \"children\": [\n                {\n                \"title\" : \"title\",\n                \"typeCode\": 2,\n                \"children\": [\n                    {\n                      \"dateAdded\": 1269200039653000,\n                      \"guid\": \"xxxydfalkj\",\n                      \"id\": 113,\n                      \"index\": 0,\n                      \"lastModified\": 1305978154986000,\n                      \"title\": \"entry title\",\n                      \"type\": \"text/x-moz-place\",\n                      \"typeCode\": 1,\n                      \"tags\" : \"x,y\",\n                      \"uri\": \"http://uri.com/abc?234&536\",\n                      \"annos\" : [{\n                             \"name\": \"bookmarkProperties/description\",\n                             \"value\": \"desc\"\n                       }]\n                    }]\n             }]\n        }\"\"\")\n\n    # Act\n    items = import_firefox_json(data)\n\n    # Assert\n    result = []\n    for item in items:\n        result.append(item)\n\n    assert len(result) == 1\n    assert result[0][0] == 'http://uri.com/abc?234&536'\n    assert result[0][1] == 'entry title'\n    assert result[0][2] == ',x,y,'\n    assert result[0][3] == 'desc'\n\n\ndef test_load_no_typecode():\n    \"\"\"test method.\"\"\"\n    # Arrange\n    data = json.loads(\"\"\"\n        {\n            \"title\" : \"main\",\n            \"typeCode\": 2,\n            \"children\": [\n                {\n                \"title\" : \"title\",\n                \"children\": [\n                    {\n                        \"title\" : \"title1\",\n                        \"uri\" : \"http://uri1\",\n                        \"annos\" : [{\n                             \"name\": \"bookmarkProperties/description\",\n                             \"value\": \"desc\"\n                         }]\n                    }]\n                }]\n        }\"\"\")\n\n    # Act\n    items = import_firefox_json(data)\n\n    # Assert\n    result = []\n    for item in items:\n        result.append(item)\n\n    assert len(result) == 0\n\n\ndef test_load_invalid_typecode():\n    \"\"\"test method.\"\"\"\n    # Arrange\n    data = json.loads(\"\"\"\n        {\n            \"title\" : \"title\",\n            \"children\": [\n                {\n                    \"title\" : \"title1\",\n                    \"typeCode\" : 99,\n                    \"uri\" : \"http://uri1\",\n                    \"annos\" : [{\n                         \"name\": \"bookmarkProperties/description\",\n                         \"value\": \"desc\"\n                     }]\n                }]\n        }\"\"\")\n    # Act\n    items = import_firefox_json(data)\n\n    # Assert\n    result = []\n    for item in items:\n        result.append(item)\n\n    assert len(result) == 0\n\ndef test_load_folder_with_no_children():\n    \"\"\"test method.\"\"\"\n\n    # Arrange\n    data = json.loads(\"\"\"\n        {\n            \"title\" : \"title\",\n            \"typeCode\" : 2\n        } \"\"\")\n\n    # Act\n    items = import_firefox_json(data)\n\n    # Assert\n    result = []\n    for item in items:\n        result.append(item)\n\n    assert len(result) == 0\n\ndef test_load_one_child():\n    \"\"\"test method.\"\"\"\n\n    # Arrange\n    data = json.loads(\"\"\"\n        {\n            \"title\" : \"main\",\n            \"typeCode\" : 2,\n            \"children\": [\n                {\n                \"title\" : \"title\",\n                \"typeCode\" : 2,\n                \"children\": [\n                    {\n                        \"title\" : \"title1\",\n                        \"typeCode\" : 1,\n                        \"uri\" : \"http://uri1\",\n                        \"annos\" : [{\n                             \"name\": \"bookmarkProperties/description\",\n                             \"value\": \"desc\"\n                          }]\n                     }\n                ]}\n           ]\n        }\n    \"\"\")\n\n    # Act\n    items = import_firefox_json(data)\n\n    # Assert\n    result = []\n    for item in items:\n        result.append(item)\n\n    assert len(result) == 1\n    assert result[0][0] == 'http://uri1'\n    assert result[0][1] == 'title1'\n    assert result[0][2] == ','\n    assert result[0][3] == 'desc'\n\ndef test_load_one_container_child():\n    \"\"\"test method.\"\"\"\n\n    # Arrange\n    data = json.loads(\"\"\"\n        {\n            \"title\" : \"main\",\n            \"typeCode\": 2,\n            \"children\": [\n                {\n                \"title\" : \"title\",\n                \"typeCode\" : 2,\n                \"children\": [\n                    {\n                        \"title\":\"bookmark folder\",\n                        \"typeCode\":2\n                    }]\n             }]\n         }\"\"\")\n\n    # Act\n    items = import_firefox_json(data)\n\n    # Assert\n    result = []\n    for item in items:\n        result.append(item)\n\n    assert len(result) == 0\n\ndef test_load_many_children():\n    \"\"\"test method.\"\"\"\n\n    # Arrange\n    data = json.loads(\"\"\"\n        {\n            \"title\" : \"main\",\n            \"typeCode\" : 2,\n            \"children\": [\n                {\n                \"title\":\"Weitere Lesezeichen\",\n                \"typeCode\":2,\n                \"children\": [\n                    {\"title\":\"title1\",\"typeCode\":1,\"uri\":\"http://uri1.com/#more-74\"},\n                    {\"title\":\"title2\",\"typeCode\":1,\"uri\":\"http://uri2.com/xyz\"},\n                    {\"title\":\"title3\",\"typeCode\":1,\"uri\":\"http://uri3.com\"}\n                ]}\n            ]\n        }\n    \"\"\")\n\n    # Act\n    items = import_firefox_json(data)\n\n    # Assert\n    result = []\n    for item in items:\n        result.append(item)\n\n    assert len(result) == 3\n\ndef test_load_container_no_title():\n    \"\"\"test method.\"\"\"\n\n    # Arrange\n    data = json.loads(\"\"\"\n        {\n            \"title\" : \"main\",\n            \"typeCode\" : 2,\n            \"children\": [\n                {\n                \"typeCode\" : 2,\n                \"children\": [\n                        {\"title\":\"title1\",\"typeCode\":1,\"uri\":\"http://uri.com\"}\n                ]}\n            ]\n        }\n    \"\"\")\n\n    # Act\n    items = import_firefox_json(data, add_bookmark_folder_as_tag=True)\n\n    # Assert\n    result = []\n    for item in items:\n        result.append(item)\n\n    assert len(result) == 1\n    assert result[0][0] == 'http://uri.com'\n    assert result[0][2] == ',<no title>,'\n\ndef test_load_hierarchical_container_without_ignore():\n    \"\"\"test method.\"\"\"\n\n    # Arrange\n    data = json.loads(\"\"\"\n        {\n            \"title\" : \"main\",\n            \"typeCode\" : 2,\n            \"children\": [\n                {\n                \"title\" : \"title\",\n                \"typeCode\" : 2,\n                \"children\": [\n                     {\"title\":\"title1\",\"typeCode\":1,\"uri\":\"http://uri.com\"}\n                ]\n            }]\n       }\n    \"\"\")\n\n    # Act\n    items = import_firefox_json(data, add_bookmark_folder_as_tag=True)\n\n    # Assert\n    result = []\n    for item in items:\n        result.append(item)\n\n    assert len(result) == 1\n    assert result[0][0] == 'http://uri.com'\n    assert result[0][2] == ',title,'\n\ndef test_load_hierarchical_container_with_ignore():\n    \"\"\"test method.\"\"\"\n\n    # Arrange\n    data = json.loads(\"\"\"\n        {\n            \"title\" : \"main\",\n            \"typeCode\" : 2,\n            \"children\": [\n                {\n                \"title\" : \"title\",\n                \"typeCode\" : 2,\n                \"root\": \"bookmarksMenuFolder\",\n                \"children\": [\n                        {\n                            \"title\" : \"title2\",\n                            \"typeCode\" : 2,\n                            \"children\": [\n                                {\"title\":\"title1\",\"typeCode\":1,\"uri\":\"http://uri1.com/#more-74\"}\n                            ]\n                        },\n                        {\"title\":\"title4\",\"typeCode\":1,\"uri\":\"http://uri4.com/#more-74\"}\n                ]\n            }]\n       }\n    \"\"\")\n\n    # Act\n    items = import_firefox_json(data, add_bookmark_folder_as_tag=True)\n\n    # Assert\n    result = []\n    for item in items:\n        result.append(item)\n\n    assert len(result) == 2\n    assert result[0][0] == 'http://uri1.com/#more-74'\n    assert result[1][0] == 'http://uri4.com/#more-74'\n\n    assert result[0][2] == ',title2,'\n    assert result[1][2] == ','\n\ndef test_load_separator():\n    \"\"\"test method.\"\"\"\n\n    # Arrange\n    data = json.loads(\"\"\"\n        {\n            \"title\" : \"main\",\n            \"typeCode\" : 2,\n            \"children\": [\n                {\n                \"title\" : \"title\",\n                \"typeCode\" : 2,\n                \"children\": [\n                    {\n                      \"title\": \"\",\n                      \"type\": \"text/x-moz-place-separator\",\n                      \"typeCode\": 3\n                    }]\n            }]\n         }\"\"\")\n\n    # Act\n    items = import_firefox_json(data)\n\n    # Assert\n    result = []\n    for item in items:\n        result.append(item)\n\n    assert len(result) == 0\n\ndef test_load_multiple_tags():\n    \"\"\"test method.\"\"\"\n    # Arrange\n    data = json.loads(\"\"\"\n        {\n            \"title\" : \"main\",\n            \"typeCode\": 2,\n            \"children\": [\n                {\n                \"title\" : \"title\",\n                \"typeCode\": 2,\n                \"children\": [\n                    {\n                        \"title\" : \"title1\",\n                        \"uri\" : \"http://uri1\",\n                        \"tags\" : \"tag1, tag2\",\n                        \"typeCode\": 1,\n                        \"annos\" : [{\n                             \"name\": \"bookmarkProperties/description\",\n                             \"value\": \"desc\"\n                         }]\n                    }]\n            }]\n        }\"\"\")\n\n    # Act\n    items = import_firefox_json(data)\n\n    # Assert\n    result = []\n    for item in items:\n        result.append(item)\n\n    assert len(result) == 1\n    assert result[0][2] == \",tag1,tag2,\"\n"
  },
  {
    "path": "tests/test_requirements.py",
    "content": "import pathlib\nimport sys\nfrom typing import Any\n\nimport pytest\n\nif sys.version_info >= (3, 11):\n    import tomllib\nelse:\n    import tomli as tomllib\n\nROOT_DIR = pathlib.Path(__file__).parents[1]\n\n\n@pytest.fixture(scope='module')\ndef pyproject() -> dict[str, Any]:\n    data = (ROOT_DIR / 'pyproject.toml').read_text()\n    return tomllib.loads(data)['project']\n\n\n_reqs = lambda path: [s for s in pathlib.Path(path).read_text(encoding='utf8', errors='surrogateescape').splitlines()\n                      if not s.startswith('#') and s != 'setuptools']\n\n\ndef test_bukuserver_requirement(pyproject: dict[str, Any]):\n    assert sorted(_reqs('bukuserver/requirements.txt')) == sorted(pyproject['optional-dependencies']['server'])\n\n\ndef test_buku_requirement(pyproject: dict[str, Any]):\n    assert sorted(_reqs('requirements.txt')) == sorted(pyproject['dependencies'])\n"
  },
  {
    "path": "tests/test_server.py",
    "content": "import os\nfrom typing import Any, Dict\nfrom http import HTTPStatus\nimport pytest\nimport flask\nfrom click.testing import CliRunner\nfrom buku import FetchResult\nfrom bukuserver import server\nfrom bukuserver.response import Response\nfrom bukuserver.server import get_bool_from_env_var\nfrom tests.util import mock_fetch\n\n\ndef assert_response(response, exp_res: Response, data: Dict[str, Any] = None):\n    assert response.status_code == exp_res.status_code\n    assert response.get_json() == exp_res.json(data=data)\n\n\n@pytest.mark.parametrize(\n    'data, exp_json', [\n        [None, {'status': 0, 'message': 'Success.'}],\n        [{}, {'status': 0, 'message': 'Success.'}],\n        [{'key': 'value'}, {'status': 0, 'message': 'Success.', 'key': 'value'}],\n    ]\n)\ndef test_response_json(data, exp_json):\n    assert Response.SUCCESS.json(data=data) == exp_json\n\n\n@pytest.mark.parametrize(\n    'args,word',\n    [\n        ('--help', 'bukuserver'),\n        ('--version', 'buku')\n    ]\n)\ndef test_cli(args, word):\n    runner = CliRunner()\n    result = runner.invoke(server.cli, [args])\n    assert result.exit_code == 0\n    assert word in result.output\n\n\n@pytest.fixture\ndef client(tmp_path):\n    test_db = tmp_path / 'test.db'\n    app = server.create_app(test_db.as_posix())\n    client = app.test_client()\n    yield client\n    flask.g.bukudb.close()\n    os.remove(test_db)\n\n\ndef test_home(client):\n    rd = client.get('/')\n    assert rd.status_code == 200\n    assert not flask.g.bukudb.get_rec_all()\n\n\n@pytest.mark.parametrize('method, url, exp_res, data', [\n    ('get', '/api/tags', Response.SUCCESS, {'tags': []}),\n    ('get', '/api/bookmarks', Response.SUCCESS, {'bookmarks': []}),\n    ('get', '/api/bookmarks/search?keywords=x', Response.SUCCESS, {'bookmarks': []}),\n    ('post', '/api/bookmarks/refresh', Response.FAILURE, None),\n])\ndef test_api_empty_db(client, method, url, exp_res, data):\n    rd = getattr(client, method)(url)\n    assert_response(rd, exp_res, data)\n\n\n@pytest.mark.parametrize('url, methods', [\n    ('api/tags', ['post', 'put', 'delete']),\n    ('/api/tags/tag1', ['post']),\n    ('api/bookmarks', ['put']),\n    ('/api/bookmarks/1', ['post']),\n    ('/api/bookmarks/refresh', ['get', 'put', 'delete']),\n    ('api/bookmarks/1/refresh', ['get', 'put', 'delete']),\n    ('/api/bookmarks/1/2', ['post']),\n])\ndef test_api_not_allowed(client, url, methods):\n    for method in methods:\n        rd = getattr(client, method)(url)\n        assert rd.status_code == HTTPStatus.METHOD_NOT_ALLOWED.value\n\n\n@pytest.mark.parametrize('method, url, json, exp_res', [\n    ('get', '/api/tags/tag1', None, Response.TAG_NOT_FOUND),\n    ('put', '/api/tags/tag1', {'tags': ['tag2']}, Response.TAG_NOT_FOUND),\n    ('delete', '/api/tags/tag1', None, Response.TAG_NOT_FOUND),\n    ('get', '/api/tags/tag1,tag2', None, Response.TAG_NOT_VALID),\n    ('put', '/api/tags/tag1,tag2', {'tags': ['tag2']}, Response.TAG_NOT_VALID),\n    ('delete', '/api/tags/tag1,tag2', None, Response.TAG_NOT_VALID),\n    ('get', '/api/bookmarks/1', None, Response.BOOKMARK_NOT_FOUND),\n    ('put', '/api/bookmarks/1', {'title': 'none'}, Response.BOOKMARK_NOT_FOUND),\n    ('delete', '/api/bookmarks/1', None, Response.BOOKMARK_NOT_FOUND),\n    ('post', '/api/bookmarks/1/refresh', None, Response.BOOKMARK_NOT_FOUND),\n    ('get', '/api/bookmarks/1/2', None, Response.RANGE_NOT_VALID),\n    ('put', '/api/bookmarks/1/2', {1: {'title': 'one'}, 2: {'title': 'two'}}, Response.RANGE_NOT_VALID),\n    ('delete', '/api/bookmarks/1/2', None, Response.RANGE_NOT_VALID),\n])\ndef test_api_invalid_id(client, method, url, json, exp_res):\n    rd = getattr(client, method)(url, json=json)\n    assert_response(rd, exp_res)\n\n\ndef test_api_tag(client):\n    url = 'http://google.com'\n    with mock_fetch(title='Google'):\n        rd = client.post('/api/bookmarks', json={'url': url, 'tags': ['tag1', 'TAG2'], 'fetch': True})\n    assert_response(rd, Response.SUCCESS, {'index': 1})\n    rd = client.get('/api/tags')\n    assert_response(rd, Response.SUCCESS, {'tags': ['tag1', 'tag2']})\n    rd = client.get('/api/tags/tag1')\n    assert_response(rd, Response.SUCCESS, {'name': 'tag1', 'usage_count': 1})\n    rd = client.put('/api/tags/tag1', json={'tags': 'string'})\n    assert_response(rd, Response.INPUT_NOT_VALID, data={'errors': {'tags': ['Invalid input.']}})\n    for json in [{}, {'tags': None}, {'tags': ''}, {'tags':[]}]:\n        rd = client.put('/api/tags/tag1', json={'tags': []})\n        assert_response(rd, Response.INPUT_NOT_VALID, data={'errors': {'tags': ['This field is required.']}})\n    rd = client.put('/api/tags/tag1', json={'tags': ['ok', '', None]})\n    errors = {'tags': [[], ['Invalid input.'], ['The value must be a string.']]}\n    assert_response(rd, Response.INPUT_NOT_VALID, data={'errors': errors})\n    rd = client.put('/api/tags/tag1', json={'tags': ['one,two', 3,]})\n    errors = {'tags': [['Invalid input.'], ['The value must be a string.']]}\n    assert_response(rd, Response.INPUT_NOT_VALID, data={'errors': errors})\n    rd = client.put('/api/tags/tag1', json={'tags': ['tag3', 'TAG 4']})\n    assert_response(rd, Response.SUCCESS)\n    rd = client.get('/api/tags')\n    assert_response(rd, Response.SUCCESS, {'tags': ['tag 4', 'tag2', 'tag3']})\n    rd = client.put('/api/tags/tag 4', json={'tags': ['tag5']})\n    assert_response(rd, Response.SUCCESS)\n    rd = client.get('/api/tags')\n    assert_response(rd, Response.SUCCESS, {'tags': ['tag2', 'tag3', 'tag5']})\n    rd = client.delete('/api/tags/tag3')\n    assert_response(rd, Response.SUCCESS)\n    rd = client.delete('/api/tags/tag3')\n    assert_response(rd, Response.TAG_NOT_FOUND)\n    rd = client.delete('/api/tags/tag,2')\n    assert_response(rd, Response.TAG_NOT_VALID)\n    rd = client.get('/api/bookmarks/1')\n    assert_response(rd, Response.SUCCESS, {'description': '', 'tags': ['tag2', 'tag5'], 'title': 'Google', 'url': url})\n\n\ndef test_api_bookmark(client):\n    url = 'http://google.com'\n    rd = client.post('/api/bookmarks', json={})\n    errors = {'url': ['This field is required.']}\n    assert_response(rd, Response.INPUT_NOT_VALID, data={'errors': errors})\n    with mock_fetch(title='Google'):\n        rd = client.post('/api/bookmarks', json={'url': url, 'fetch': True})\n        assert_response(rd, Response.SUCCESS, {'index': 1})\n        rd = client.post('/api/bookmarks', json={'url': url, 'fetch': True})\n        assert_response(rd, Response.FAILURE)\n    rd = client.get('/api/bookmarks')\n    assert_response(rd, Response.SUCCESS, {'bookmarks': [{'description': '', 'tags': [], 'title': 'Google', 'url': url}]})\n    rd = client.get('/api/bookmarks/1')\n    assert_response(rd, Response.SUCCESS, {'description': '', 'tags': [], 'title': 'Google', 'url': url})\n    rd = client.put('/api/bookmarks/1', json={'tags': 'not a list'})\n    assert_response(rd, Response.INPUT_NOT_VALID, data={'errors': {'tags': ['Invalid input.']}})\n    rd = client.put('/api/bookmarks/1', json={'tags': ['tag1', 'tag2']})\n    assert_response(rd, Response.SUCCESS)\n    with mock_fetch(title='Google'):\n        rd = client.put('/api/bookmarks/1', json={'fetch': True})\n    assert_response(rd, Response.SUCCESS)\n    rd = client.get('/api/bookmarks/1')\n    assert_response(rd, Response.SUCCESS, {'description': '', 'tags': ['tag1', 'tag2'], 'title': 'Google', 'url': url})\n    rd = client.put('/api/bookmarks/1', json={'tags': [], 'description': 'Description'})\n    assert_response(rd, Response.SUCCESS)\n    rd = client.get('/api/bookmarks/1')\n    assert_response(rd, Response.SUCCESS, {'description': 'Description', 'tags': [], 'title': 'Google', 'url': url})\n\n\n@pytest.mark.parametrize('d_url', ['/api/bookmarks', '/api/bookmarks/1'])\ndef test_api_bookmark_delete(client, d_url):\n    url = 'http://google.com'\n    rd = client.post('/api/bookmarks', json={'url': url, 'fetch': False})\n    assert_response(rd, Response.SUCCESS, {'index': 1})\n    rd = client.delete(d_url)\n    assert_response(rd, Response.SUCCESS)\n\n\n@pytest.mark.parametrize('api_url', ['/api/bookmarks/refresh', '/api/bookmarks/1/refresh'])\ndef test_api_bookmark_refresh(client, api_url):\n    url = 'http://google.com'\n    with mock_fetch(title='Google'):\n        rd = client.post('/api/bookmarks', json={'url': url})\n        assert_response(rd, Response.SUCCESS, {'index': 1})\n        rd = client.post(api_url)\n        assert_response(rd, Response.SUCCESS)\n    rd = client.get('/api/bookmarks/1')\n    assert_response(rd, Response.SUCCESS, {'description': '', 'tags': [], 'title': 'Google', 'url': url})\n\n\n@pytest.mark.parametrize('kwargs, kwmock, exp_res, data', [\n    (\n        {'data': {'url': 'http://google.com'}},\n        {'title': 'Google', 'fetch_status': 200},\n        Response.SUCCESS,\n        {'bad url': 0, 'recognized mime': 0, 'tags': '', 'title': 'Google'}\n    ),\n    ({}, {}, Response.INPUT_NOT_VALID, {'errors': {'url': ['This field is required.']}}),\n    (\n        {'data': {'url': 'chrome://bookmarks/'}},\n        {'bad': True},\n        Response.SUCCESS,\n        {'bad url': 1, 'recognized mime': 0, 'tags': '', 'title': ''}\n    ),\n])\n@pytest.mark.parametrize('endpoint', ['/api/fetch_data', '/api/network_handle'])\ndef test_api_fetch_data(client, endpoint, kwargs, kwmock, exp_res, data):\n    with mock_fetch(**kwmock):\n        rd = client.post(endpoint, **kwargs)\n    assert rd.status_code == exp_res.status_code\n    rd_json = rd.get_json()\n    rd_json.pop('description', None)\n    if endpoint == '/api/fetch_data' and exp_res is Response.SUCCESS:\n        data = FetchResult(kwargs['data']['url'], **kwmock)._asdict()\n    assert rd_json == exp_res.json(data=data)\n\n\ndef test_api_bookmark_range(client):\n    bookmarks = [('http://google.com', 'Google'),\n                 ('http://example.com', 'Example Domain')]\n    for index, (url, title) in enumerate(bookmarks, start=1):\n        with mock_fetch(title=title):\n            rd = client.post('/api/bookmarks', json={'url': url, 'fetch': True})\n        assert_response(rd, Response.SUCCESS, {'index': index})\n\n    rd = client.put('/api/bookmarks/1/2', json={\n        '1': {'tags': ['tag1 A', 'tag1 B', 'tag1 C']},\n        '2': {'tags': ['tag2']}\n    })\n    assert_response(rd, Response.SUCCESS)\n    rd = client.get('/api/bookmarks/1/2')\n    assert_response(rd, Response.SUCCESS, {'bookmarks': {\n        '1': {'description': '', 'tags': ['tag1 a', 'tag1 b', 'tag1 c'], 'title': 'Google', 'url': 'http://google.com'},\n        '2': {'description': '', 'tags': ['tag2',], 'title': 'Example Domain', 'url': 'http://example.com'}}})\n    rd = client.put('/api/bookmarks/1/2', json={\n        '1': {'title': 'Bookmark 1', 'tags': ['tag1 C', 'tag1 A'], 'del_tags': True},\n        '2': {'title': 'Bookmark 2', 'tags': ['-', 'tag2'], 'del_tags': False}\n    })\n    assert_response(rd, Response.SUCCESS)\n    rd = client.get('/api/bookmarks/1/2')\n    assert_response(rd, Response.SUCCESS, {'bookmarks': {\n        '1': {'description': '', 'tags': ['tag1 b'], 'title': 'Bookmark 1', 'url': 'http://google.com'},\n        '2': {'description': '', 'tags': ['-', 'tag2',], 'title': 'Bookmark 2', 'url': 'http://example.com'}}})\n\n    rd = client.put('/api/bookmarks/2/1', json={})\n    assert_response(rd, Response.RANGE_NOT_VALID)\n\n    rd = client.put('/api/bookmarks/1/2', json={})\n    assert_response(rd, Response.INPUT_NOT_VALID, data={\n        'errors': {\n            '1': 'Input required.',\n            '2': 'Input required.'\n        }\n    })\n    rd = client.put('/api/bookmarks/1/2', json={'1': {'tags': []}})\n    assert_response(rd, Response.INPUT_NOT_VALID, data={'errors': {'2': 'Input required.'}})\n    rd = client.put('/api/bookmarks/1/2', json={\n        '1': {'tags': ['ok', 'with,delim']},\n        '2': {'tags': 'string'},\n    })\n    assert_response(rd, Response.INPUT_NOT_VALID, data={\n        'errors': {\n            '1': {'tags': [[], ['Invalid input.']]},\n            '2': {'tags': ['Invalid input.']}\n        }\n    })\n    rd = client.get('/api/bookmarks/2/1')\n    assert_response(rd, Response.RANGE_NOT_VALID)\n    rd = client.delete('/api/bookmarks/1/2')\n    assert_response(rd, Response.SUCCESS)\n    rd = client.get('/api/bookmarks')\n    assert_response(rd, Response.SUCCESS, {'bookmarks': []})\n\n\ndef test_api_bookmark_search(client):\n    with mock_fetch(title='Google'):\n        rd = client.post('/api/bookmarks', json={'url': 'http://google.com', 'fetch': True})\n    assert_response(rd, Response.SUCCESS, {'index': 1})\n    rd = client.get('/api/bookmarks/search', query_string={'keywords': ['google']})\n    assert_response(rd, Response.SUCCESS, {'bookmarks': [\n        {'description': '', 'index': 1, 'tags': [], 'title': 'Google', 'url': 'http://google.com'}]})\n    rd = client.delete('/api/bookmarks/search', data={'keywords': ['google']})\n    assert_response(rd, Response.SUCCESS, {'deleted': 1})\n    rd = client.get('/api/bookmarks')\n    assert_response(rd, Response.SUCCESS, {'bookmarks': []})\n\n\n@pytest.mark.parametrize('env_val, exp_val', [\n    ['true', True],\n    ['false', False],\n    ['0', False],\n    ['1', True],\n    [None, True],\n    ['random', True]\n])\ndef test_get_bool_from_env_var(monkeypatch, env_val, exp_val):\n    key = 'BUKUSERVER_TEST'\n    if env_val is not None:\n        monkeypatch.setenv(key, env_val)\n    assert get_bool_from_env_var(key, True) == exp_val\n"
  },
  {
    "path": "tests/test_views.py",
    "content": "\"\"\"test for views.\n\nresources: https://flask.palletsprojects.com/en/2.2.x/testing/\n\"\"\"\nimport os\nfrom argparse import Namespace\nfrom unittest import mock\n\nimport pytest\nimport flask\nfrom flask import request\nfrom lxml import etree\nfrom werkzeug.datastructures import MultiDict\n\nfrom buku import BukuDb\nfrom bukuserver import server\nfrom bukuserver.views import BookmarkModelView, TagModelView, filter_key\nfrom tests.util import mock_fetch, _add_rec\n\n\n@pytest.fixture()\ndef dbfile(tmp_path):\n    return (tmp_path / \"test.db\").as_posix()\n\n@pytest.fixture()\ndef app(dbfile):\n    app = server.create_app(dbfile)\n    app.config.update({'TESTING': True, 'WTF_CSRF_ENABLED': False})\n    # other setup can go here\n    yield app\n    # clean up / reset resources here\n    flask.g.bukudb.close()\n    if os.path.exists(dbfile):\n        os.remove(dbfile)\n\ndef env_fixture(name, **kwargs):  # place this fixture BEFORE app or its dependencies\n    \"\"\"Produces a fixture that mocks a test parameter directly in an env var (before app init)\"\"\"\n    def _env_fixture(dbfile, monkeypatch, request):\n        if request.param is not None:  # default value placeholder\n            monkeypatch.setenv(name, str(request.param))\n        app = server.create_app(dbfile)\n        app.config.update({'TESTING': True, 'WTF_CSRF_ENABLED': False})\n        yield request.param\n        flask.g.bukudb.close()\n        if os.path.exists(dbfile):\n            os.remove(dbfile)\n    return pytest.fixture(**kwargs)(_env_fixture)\n\n\n@pytest.fixture()\ndef client(app):\n    return app.test_client()\n\n@pytest.fixture()\ndef runner(app):\n    return app.test_cli_runner()\n\n@pytest.fixture()\ndef bukudb(dbfile):\n    bdb = BukuDb(dbfile=dbfile)\n    yield bdb\n    bdb.close()\n    if os.path.exists(dbfile):\n        os.remove(dbfile)\n\n@pytest.fixture\ndef tmv_instance(bukudb):\n    \"\"\"define tag model view instance\"\"\"\n    return TagModelView(bukudb)\n\n@pytest.fixture\ndef bmv_instance(bukudb):\n    \"\"\"define tag model view instance\"\"\"\n    return BookmarkModelView(bukudb)\n\n\n@pytest.mark.parametrize('idx, char', [('', ''), (0, '0'), (9, '9'), (10, 'A'), (35, 'Z'), (36, 'a'), (61, 'z')])\ndef test_filter_key(idx, char):\n    with mock.patch('bukuserver.views.BookmarkModelView._filter_arg', return_value='filter_name'):\n        assert filter_key(None, idx) == f'flt{char}_filter_name'\n\n\n@pytest.mark.parametrize('disable_favicon', [False, True])\ndef test_bookmark_model_view(bukudb, disable_favicon, app):\n    inst = BookmarkModelView(bukudb)\n    model = Namespace(description=\"randomdesc\", id=1, tags=\"tags1\", title=\"Example Domain\", url=\"http://example.com\")\n    app.config[\"BUKUSERVER_DISABLE_FAVICON\"] = disable_favicon\n    with app.test_request_context():\n        assert inst._list_entry(None, model, \"Entry\")\n\n\ndef test_tag_model_view_get_list_empty_db(tmv_instance):\n    res = tmv_instance.get_list(None, None, None, None, [])\n    assert res == (0, [])\n\n\n@pytest.mark.parametrize(\n    \"sort_field, sort_desc, filters, exp_res\",\n    [\n        [None, False, [], (0, [])],\n        [None, False, [(0, \"name\", \"t2\")], (0, [])],\n        [\"name\", False, [], (0, [])],\n        [\"name\", True, [], (0, [])],\n        [\"usage_count\", True, [], (0, [])],\n    ],\n)\ndef test_tag_model_view_get_list(tmv_instance, sort_field, sort_desc, filters, exp_res):\n    _add_rec(tmv_instance.bukudb, 'http://example.com/1.jpg', tags_in='t1,t2,t3')\n    _add_rec(tmv_instance.bukudb, 'http://example.com/2.jpg', tags_in='t2,t3')\n    _add_rec(tmv_instance.bukudb, 'http://example.com/3.jpg', tags_in='t3')\n    res = tmv_instance.get_list(0, sort_field, sort_desc, None, filters)\n    assert res == exp_res\n\n\n@pytest.mark.parametrize('url, backlink', [\n    ['http://example.com', None],\n    ['http://example.com', '/bookmark/'],\n])\ndef test_bmv_create_form(bmv_instance, url, backlink, app):\n    with app.test_request_context():\n        request.args = MultiDict({'link': url, 'url': backlink} if backlink else {'link': url})\n        form = bmv_instance.create_form()\n        assert form.url.data == url\n\n\n#\n# -= functional tests =-\n#\n\nxpath_alert = lambda kind, message: f'//div[@class=\"alert alert-{kind} alert-dismissable\"][contains(., \"{message}\")]'\nxpath_cls = lambda s: ''.join(f'[contains(concat(\" \", @class, \" \"), \" {s} \")]' for s in s.split(' ') if s)\n\ndef assert_success_alert(dom, edit, id=1):\n    message = f'Record was successfully {\"saved\" if edit else \"created\"}.'\n    assert dom.xpath(xpath_alert('success', message)), 'alert missing'\n    assert dom.xpath(f'//script[contains(., \"const SUCCESS = [\")][contains(., \"{message}\")][contains(., \"/bookmark/details/?id={id}&\")]')\n\ndef assert_failure_alert(dom, edit):\n    assert dom.xpath(xpath_alert('danger', f'Failed to {\"update\" if edit else \"create\"} record. Duplicate URL')), 'alert missing'\n    assert not dom.xpath('//script[contains(., \"const SUCCESS = [\")][contains(., \"/bookmark/details/?id=\")]')\n\ndef assert_response(response, uri, *, status=200, argnames=None, args=None):\n    assert response.status_code == status\n    assert response.request.path == uri\n    if argnames is not None:\n        assert set(response.request.args) == set(argnames)\n    if args is not None:\n        assert dict(response.request.args) == args\n    return etree.HTML(response.text)\n\ndef assert_bookmark(bookmark, query, tags=None):\n    assert bookmark.url == query['link']\n    assert bookmark.title == query['title']\n    assert bookmark.desc == query['description']\n    assert bookmark.tags == tags or query['tags']\n\n\n@pytest.mark.gui\n@pytest.mark.slow\n@pytest.mark.parametrize('exists, uri, tab, args', [\n    (False, '/bookmark/new/', 'Create', ['link', 'title', 'description', 'popup']),\n    (True, '/bookmark/edit/', 'Edit', ['id', 'popup']),\n])\ndef test_bookmarklet_view(bukudb, client, exists, uri, tab, args):\n    query = {'url': 'http://example.com', 'title': 'Sample site', 'description': 'Foo bar baz'}\n    if exists:\n        _add_rec(bukudb, query['url'])\n\n    response = client.get('/bookmarklet', query_string=query, follow_redirects=True)\n    dom = assert_response(response, uri, argnames=args)\n    assert dom.xpath(f'//ul{xpath_cls(\"nav nav-tabs\")}//a{xpath_cls(\"nav-link active\")}/text()') == [tab]\n    assert dom.xpath('//input[@name=\"link\"]/@value') == [query['url']]\n    assert bool(dom.xpath('//input[@name=\"id\"]')) == exists\n\n\n@pytest.mark.gui\n@pytest.mark.slow\n@pytest.mark.parametrize('fetch, title, desc', [\n    (True, 'Some title', ''),\n    (True, '', 'Some description'),\n    (False, 'Some title', ''),\n    (False, '', 'Some description'),\n    (None, 'Some title', ''),\n    (None, '', 'Some description'),\n])\ndef test_create_and_fetch(bukudb, monkeypatch, client, fetch, title, desc):\n    query = {'link': 'http://example.com', 'title': title, 'description': desc, 'tags': 'foo, bar, baz'}\n    _title, _desc = 'Fetched title', 'Fetched description'\n    if fetch is not None:\n        query['fetch'] = 'on' if fetch else ''\n\n    with mock_fetch(title=_title, desc=_desc):\n        response = client.post('/bookmark/new/', data=query, follow_redirects=True)\n    dom = assert_response(response, '/bookmark/')\n    assert_success_alert(dom, edit=False)\n    [bookmark] = bukudb.get_rec_all()\n    assert_bookmark(bookmark, {\n        'link': query['link'], 'tags': ',bar,baz,foo,',\n        'title': (title or _title) if fetch or fetch is None else title,  # defaults to True\n        'description': (desc or _desc) if fetch or fetch is None else desc,\n    })\n\n\n@pytest.mark.gui\n@pytest.mark.slow\n@pytest.mark.parametrize('redirect, uri, args', [\n    ('_add_another', '/bookmark/new/', {}),\n    ('_continue_editing', '/bookmark/edit/', {'id': '1', 'url': '/bookmark/'}),\n])\ndef test_create_redirect(client, redirect, uri, args):\n    query = {'link': 'http://example.com', 'title': '', 'description': '', 'tags': '', 'fetch': '', redirect: 'on'}\n\n    response = client.post('/bookmark/new/', data=query, follow_redirects=True)\n    dom = assert_response(response, uri, args=args)\n    assert_success_alert(dom, edit=False)\n\n\n@pytest.mark.gui\n@pytest.mark.slow\ndef test_create_duplicate(bukudb, client):\n    query = {'link': 'http://example.com', 'title': '', 'description': '', 'tags': ''}\n    _add_rec(bukudb, query['link'])\n\n    response = client.post('/bookmark/new/', data=query, follow_redirects=True)\n    dom = assert_response(response, '/bookmark/new/')\n    assert_failure_alert(dom, edit=False)\n\n\n@pytest.mark.gui\n@pytest.mark.slow\n@pytest.mark.parametrize('override', [False, True])\ndef test_update(bukudb, client, override):\n    _add_rec(bukudb, 'http://example.org')\n    query = {'link': 'http://example.com', 'title': 'Sample site', 'description': 'Foo bar baz', 'tags': 'foo, bar, baz'}\n    if override:\n        _add_rec(bukudb, query['link'])\n\n    response = client.post('/bookmark/edit/', query_string={'id': 1}, data=query, follow_redirects=True)\n    if override:\n        dom = assert_response(response, '/bookmark/edit/')\n        assert_failure_alert(dom, edit=True)\n    else:\n        dom = assert_response(response, '/bookmark/')\n        assert_success_alert(dom, edit=True)\n        [bookmark] = bukudb.get_rec_all()\n        assert_bookmark(bookmark, query, tags=',bar,baz,foo,')\n\n\n@pytest.mark.gui\n@pytest.mark.slow\n@pytest.mark.parametrize('redirect, uri, args', [\n    ('_add_another', '/bookmark/new/', {'url': '/bookmark/'}),\n    ('_continue_editing', '/bookmark/edit/', {'id': '1', 'url': '/bookmark/'}),\n])\ndef test_update_redirect(bukudb, client, redirect, uri, args):\n    _add_rec(bukudb, 'http://example.org')\n    query = {'link': 'http://example.com', 'title': 'Sample site', 'description': 'Foo bar baz', 'tags': 'foo, bar, baz', redirect: 'on'}\n\n    response = client.post('/bookmark/edit/', query_string={'id': 1}, data=query, follow_redirects=True)\n    dom = assert_response(response, uri, args=args)\n    assert_success_alert(dom, edit=True)\n    [bookmark] = bukudb.get_rec_all()\n    assert_bookmark(bookmark, query, tags=',bar,baz,foo,')\n\n\n@pytest.mark.gui\n@pytest.mark.slow\n@pytest.mark.parametrize('exists', [True, False])\ndef test_delete(client, bukudb, exists):\n    if exists:\n        _add_rec(bukudb, 'http://example.com')\n\n    response = client.post('/bookmark/delete/', data={'id': 1}, follow_redirects=True)\n    dom = assert_response(response, '/bookmark/')\n    assert dom.xpath(xpath_alert('success', 'Record was successfully deleted.') if exists else\n                     xpath_alert('danger', 'Record does not exist.'))\n\n\n@pytest.mark.gui\n@pytest.mark.slow\n@pytest.mark.parametrize('total, per_page, pages, last_page', [\n    (0, 5, 1, 0),\n    (1, 5, 1, 1),\n    (5, 5, 1, 5),\n    (6, 5, 2, 1),\n    (9, 5, 2, 4),\n    (10, 5, 2, 5),\n    (11, 5, 3, 1),\n    (9, None, 1, 9),\n    (10, None, 1, 10),\n    (11, None, 2, 1),\n    (14, 15, 1, 14),\n    (15, 15, 1, 15),\n    (16, 15, 2, 1),\n])\ndef test_env_per_page(bukudb, app, client, total, per_page, pages, last_page):\n    for i in range(1, total+1):\n        _add_rec(bukudb, f'http://example.com/{i}')\n    if per_page:\n        app.config.update({'BUKUSERVER_PER_PAGE': per_page})\n\n    response = client.get('/bookmark/last-page', follow_redirects=True)\n    dom = assert_response(response, '/bookmark/', args={'page': str(pages - 1)})\n    cells = dom.xpath(f'//td{xpath_cls(\"col-entry\")}')\n    assert len(cells) == last_page\n    for i, cell in enumerate(cells, start=1):\n        url = f'http://example.com/{total - last_page + i}'\n        assert cell.xpath(f'//a[@href=\"{url}\"]/text()') == ['<EMPTY TITLE>', url]\n\n\n@pytest.mark.gui\n@pytest.mark.slow\n@pytest.mark.parametrize('new_tab', [False, True, None])\n@pytest.mark.parametrize('favicons', [False, True, None])\n@pytest.mark.parametrize('mode', ['full', 'netloc', 'netloc-tag', None])\ndef test_env_entry_render_params(bukudb, app, client, mode, favicons, new_tab):\n    _test_env_entry_render_params(bukudb, app, client, mode, favicons, new_tab, 'http://example.com', 'example.com', 'Sample site')\n\n@pytest.mark.parametrize('url, netloc, title', [\n    ('http://example.com', 'example.com', ''),\n    ('javascript:void(0)', '', 'Sample site'),\n    ('javascript:void(0)', '', ''),\n])\n@pytest.mark.parametrize('mode', ['full', 'netloc', 'netloc-tag'])\ndef test_env_entry_render_params_blanks(bukudb, app, client, mode, url, netloc, title):\n    _test_env_entry_render_params(bukudb, app, client, mode, True, True, url, netloc, title)\n\ndef _test_env_entry_render_params(bukudb, app, client, mode, favicons, new_tab, url, netloc, title):\n    desc, tags = 'Foo bar baz', ',bar,baz,foo,'\n    _add_rec(bukudb, url, title, tags, desc)\n    _tags = tags.strip(',').split(',')\n    if mode:\n        app.config.update({'BUKUSERVER_URL_RENDER_MODE': mode})\n    if favicons is not None:\n        app.config.update({'BUKUSERVER_DISABLE_FAVICON': not favicons})\n    if new_tab is not None:\n        app.config.update({'BUKUSERVER_OPEN_IN_NEW_TAB': new_tab})\n\n    dom = assert_response(client.get('/bookmark/'), '/bookmark/')\n    cell = ' '.join(etree.tostring(dom.xpath(f'//td{xpath_cls(\"col-entry\")}')[0], encoding='unicode').strip().split())\n    target = '' if not new_tab else ' target=\"_blank\"'\n    icon = '' if not favicons else (netloc and f'<img class=\"favicon\" src=\"http://www.google.com/s2/favicons?domain={netloc}\"/> ')\n    urltext = title or '&lt;EMPTY TITLE&gt;'\n    _title = (urltext if not netloc and mode in ('full', None) else f'<a href=\"{url}\"{target}>{urltext}</a>')\n    prefix = f'<td class=\"col-entry\"> {icon}<span class=\"title\" title=\"{url}\">{_title}</span>'\n    tags = [f'<a class=\"btn badge badge-secondary\" href=\"/bookmark/?flt0_tags_contain={s}\">{s}</a>' for s in _tags]\n    netloc_tag = ('' if mode == 'netloc' or not netloc else\n                  f'<a class=\"btn badge badge-success\" href=\"/bookmark/?flt0_url_netloc_match={netloc}\">netloc:{netloc}</a>')\n    suffix = f'<div class=\"tag-list\">{netloc_tag}{\"\".join(tags)}</div><div class=\"description\">{desc}</div> </td>'\n    if mode == 'netloc':\n        _netloc = netloc and f'<span class=\"netloc\"> (<a href=\"/bookmark/?flt0_url_netloc_match={netloc}\">{netloc}</a>)</span>'\n        assert cell == prefix + _netloc + suffix\n    elif mode == 'netloc-tag':\n        assert cell == prefix + suffix\n    else:\n        assert cell == f'{prefix}<span class=\"link\"><a href=\"{url}\"{target}>{url}</a></span>{suffix}'\n\n\nreadonly = env_fixture('BUKUSERVER_READONLY', params=[False, True, None])\n\n@pytest.mark.gui\n@pytest.mark.slow\ndef test_env_readonly(bukudb, readonly, client):\n    _add_rec(bukudb, 'http://example.com')\n    edit = not readonly\n\n    response = client.get('/bookmark/')\n    dom = assert_response(response, '/bookmark/')\n    assert bool(dom.xpath(f'//td{xpath_cls(\"list-buttons-column\")}/a[@title=\"Edit Record\"]')) == edit, 'edit icon'\n    assert bool(dom.xpath(f'//td{xpath_cls(\"list-buttons-column\")}/form[@action=\"/bookmark/delete/\"]')) == edit, 'delete icon'\n\n    response = client.get('/bookmark/details/', query_string={'id': 1})\n    dom = assert_response(response, '/bookmark/details/')\n    assert (dom.xpath(f'//ul{xpath_cls(\"nav nav-tabs\")}/li/a/text()') ==\n            (['List', 'Details'] if readonly else ['List', 'Create', 'Edit', 'Details']))\n\n    response = client.get('/bookmark/new/', follow_redirects=True)\n    assert_response(response, '/bookmark/' if readonly else '/bookmark/new/')\n    response = client.get('/bookmark/edit/', query_string={'id': 1}, follow_redirects=True)\n    assert_response(response, '/bookmark/' if readonly else '/bookmark/edit/')\n\n\nproxy_path = env_fixture('BUKUSERVER_REVERSE_PROXY_PATH', params=['', '/buku', None])\n\n@pytest.mark.gui\n@pytest.mark.slow\ndef test_env_reverse_proxy_path(proxy_path, client):\n    links = [(proxy_path or '') + s for s in ['/', '/bookmark/', '/tag/', '/statistic/']]\n\n    dom = assert_response(client.get(links[0]), links[0])\n    assert dom.xpath(f'//nav{xpath_cls(\"navbar\")}//a/@href ') == ['/'] + links\n    body_links = dom.xpath('//main//a/@href')\n    assert body_links[-1].startswith('javascript:')\n    assert body_links[:-1] == links[1:]\n    assert dom.xpath('//main//form/@action') == [links[0]]\n\n    for link in links[1:]:\n        assert_response(client.get(link), link)\n\n\ntheme = env_fixture('BUKUSERVER_THEME', params=['default', 'slate', None])\n\n@pytest.mark.gui\n@pytest.mark.slow\ndef test_env_theme(theme, client):\n    dom = assert_response(client.get('/'), '/')\n    assert dom.xpath('//head/link[@rel=\"stylesheet\"][starts-with(@href, $href)]',\n                     href=f'/static/admin/bootstrap/bootstrap4/swatch/{theme or \"default\"}/bootstrap.min.css?')\n\n\n_DICT = {\n    'en': {f'//ul{xpath_cls(\"nav navbar-nav\")}/li/a/text()': ['Home', 'Bookmarks', 'Tags', 'Statistic'],\n           f'//ul{xpath_cls(\"nav nav-tabs\")}/li/a/text()': ['List (1)', 'Create', 'Random', 'Reorder', 'Add Filter', '10 items'],\n           f'//td{xpath_cls(\"list-buttons-column\")}/a/@title': ['View Record', 'Edit Record'],\n           f'//td{xpath_cls(\"list-buttons-column\")}/form/button/@title': ['Delete Record']},\n    'de': {f'//ul{xpath_cls(\"nav navbar-nav\")}/li/a/text()': ['Start', 'Lesezeichen', 'Schilder', 'Statistik'],\n           f'//ul{xpath_cls(\"nav nav-tabs\")}/li/a/text()':\n               ['Liste (1)', 'Erstellen', 'Zufälliger', 'Neu anordnen', 'Filter hinzufügen', '10 Elemente'],\n           f'//td{xpath_cls(\"list-buttons-column\")}/a/@title': ['Eintrag ansehen', 'Eintrag bearbeiten'],\n           f'//td{xpath_cls(\"list-buttons-column\")}/form/button/@title': ['Datenzatz löschen']},\n    'fr': {f'//ul{xpath_cls(\"nav navbar-nav\")}/li/a/text()': ['Accueil', 'Signets', 'Étiquettes', 'Statistique'],\n           f'//ul{xpath_cls(\"nav nav-tabs\")}/li/a/text()':\n               ['Liste (1)', 'Créer', 'Aléatoire', 'Réorganiser', 'Ajouter un filtre', '10 articles'],\n           f'//td{xpath_cls(\"list-buttons-column\")}/a/@title': ['Afficher L\\'enregistrement', 'Modifier enregistrement'],\n           f'//td{xpath_cls(\"list-buttons-column\")}/form/button/@title': ['Supprimer l\\'enregistrement']},\n    'ru': {f'//ul{xpath_cls(\"nav navbar-nav\")}/li/a/text()': ['Главная', 'Закладки', 'Теги', 'Статистика'],\n           f'//ul{xpath_cls(\"nav nav-tabs\")}/li/a/text()':\n               ['Список (1)', 'Создать', 'Случайная', 'Изменить порядок', 'Добавить Фильтр', '10 элементы'],\n           f'//td{xpath_cls(\"list-buttons-column\")}/a/@title': ['Просмотр записи', 'Редактировать запись'],\n           f'//td{xpath_cls(\"list-buttons-column\")}/form/button/@title': ['Удалить запись']},\n}\nlocale = env_fixture('BUKUSERVER_LOCALE', params=['en', 'de', 'fr', 'ru', None])\n\n@pytest.mark.gui\n@pytest.mark.slow\ndef test_env_locale(bukudb, locale, client):\n    strings = _DICT[locale or 'en']\n    _add_rec(bukudb, 'http://example.com')\n\n    dom = assert_response(client.get('/bookmark/'), '/bookmark/')\n    for k, v in strings.items():\n        assert [s.strip() for s in dom.xpath(k) if s.strip()] == v\n"
  },
  {
    "path": "tests/util.py",
    "content": "from unittest import mock\nimport os\n\nfrom urllib3 import HTTPResponse\n\nfrom buku import FetchResult\n\n\ndef mock_http(body=None, **kwargs):\n    body = (None if not body else str(body).encode('UTF-8'))\n    return mock.patch('urllib3.PoolManager.request', return_value=HTTPResponse(body, **kwargs))\n\ndef mock_fetch(custom=None, **kwargs):\n    _url = kwargs.pop('url', None)\n    status = kwargs.pop('fetch_status', (None if kwargs.get('bad') else 200))\n    fn = lambda url, http_head=False: FetchResult(_url or url, fetch_status=status, **kwargs)\n    return mock.patch('buku.fetch_data', side_effect=custom or fn)\n\ndef _add_rec(db, *args, **kw):\n    \"\"\"Use THIS instead of db.add_rec() UNLESS you want to wait for unnecessary network requests.\"\"\"\n    return db.add_rec(*args, fetch=False, **kw)\n\ndef _tagset(s):\n    return set(x for x in str(s or '').lower().split(',') if x)\n\ndef append(buffer, text):\n    pos = buffer.tell()\n    try:\n        buffer.seek(0, os.SEEK_END)\n        return buffer.write(text)\n    finally:\n        buffer.seek(pos)\n"
  },
  {
    "path": "tests/vcr_cassettes/test_browse_by_index.yaml",
    "content": "interactions:\n- request:\n    body: null\n    headers:\n      Accept: ['*/*']\n      Accept-Encoding: ['gzip,deflate']\n      Cookie: ['']\n      DNT: ['1']\n      User-Agent: ['Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101\n          Firefox/61.0']\n    method: GET\n    uri: https://www.google.com/ncr\n  response:\n    body: {string: \"<HTML><HEAD><meta http-equiv=\\\"content-type\\\" content=\\\"text/html;charset=utf-8\\\"\\\n        >\\n<TITLE>302 Moved</TITLE></HEAD><BODY>\\n<H1>302 Moved</H1>\\nThe document\\\n        \\ has moved\\n<A HREF=\\\"https://www.google.com/\\\">here</A>.\\r\\n</BODY></HTML>\\r\\\n        \\n\"}\n    headers:\n      Alt-Svc: ['quic=\":443\"; ma=2592000; v=\"44,43,39,35\"']\n      Cache-Control: [private]\n      Content-Length: ['220']\n      Content-Type: [text/html; charset=UTF-8]\n      Date: ['Mon, 29 Oct 2018 14:17:10 GMT']\n      Location: ['https://www.google.com/']\n      P3P: [CP=\"This is not a P3P policy! See g.co/p3phelp for more info.\"]\n      Server: [gws]\n      Set-Cookie: ['1P_JAR=2018-10-29-14; expires=Wed, 28-Nov-2018 14:17:10 GMT; path=/;\n          domain=.google.com', 'NID=144=nmrNRCo8ZiIdTt7bdJgwALmtWLPM-abnCScUe00VBvWqOCXna9soY-_3T6sRrVwS2VH3GgZJ5wqkpuL5pQ8rDaZ9e3xpjeNEvy7EuGoBoj9JTxAQr2cGdzsTKvJmJIAtyn3eCTIm0Ep_oyffK6bvY9OO2brG1R_WASXKLFGSVeY;\n          expires=Tue, 30-Apr-2019 14:17:10 GMT; path=/; domain=.google.com; HttpOnly']\n      Strict-Transport-Security: [max-age=604800]\n      X-Frame-Options: [SAMEORIGIN]\n      X-XSS-Protection: [1; mode=block]\n    status: {code: 302, message: Found}\n- request:\n    body: null\n    headers:\n      Accept: ['*/*']\n      Accept-Encoding: ['gzip,deflate']\n      Cookie: ['']\n      DNT: ['1']\n      User-Agent: ['Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101\n          Firefox/61.0']\n    method: GET\n    uri: https://www.google.com/\n  response:\n    body:\n      string: !!binary |\n        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\n        SNbzZ3wtmtVy+gMAAP//AwAKirftYzoDAA==\n    headers:\n      Alt-Svc: ['quic=\":443\"; ma=2592000; v=\"44,43,39,35\"']\n      Cache-Control: ['private, max-age=0']\n      Content-Encoding: [gzip]\n      Content-Type: [text/html; charset=UTF-8]\n      Date: ['Mon, 29 Oct 2018 14:17:11 GMT']\n      Expires: ['-1']\n      P3P: [CP=\"This is not a P3P policy! See g.co/p3phelp for more info.\"]\n      Server: [gws]\n      Set-Cookie: ['1P_JAR=2018-10-29-14; expires=Wed, 28-Nov-2018 14:17:11 GMT; path=/;\n          domain=.google.com', 'NID=144=jV4dlrQX3-8b60O9UydYFCK_463W8HCKJI3DwjzVfxLfm77Saiomv9Rfo6lRrA_1203d78XOCP3j20fKOgbTqde_SY8mP2z7sTUNY5aXgtVZc5NhxEZtPsw_IpBNsRtm3DE7yvxLAX7KLF-WQ8QtOv6uIRxbpon-0jcYH2BCVKI;\n          expires=Tue, 30-Apr-2019 14:17:11 GMT; path=/; domain=.google.com; HttpOnly']\n      Strict-Transport-Security: [max-age=604800]\n      X-Frame-Options: [SAMEORIGIN]\n      X-XSS-Protection: [1; mode=block]\n    status: {code: 200, message: OK}\nversion: 1\n"
  },
  {
    "path": "tests/vcr_cassettes/test_delete_rec_range_and_delay_commit.yaml",
    "content": "interactions:\n- request:\n    body: null\n    headers:\n      Accept: ['*/*']\n      Accept-Encoding: ['gzip,deflate']\n      Cookie: ['']\n      DNT: ['1']\n      User-Agent: ['Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101\n          Firefox/61.0']\n    method: GET\n    uri: http://slashdot.org/\n  response:\n    body: {string: \"<html>\\r\\n<head><title>301 Moved Permanently</title></head>\\r\\n\\\n        <body bgcolor=\\\"white\\\">\\r\\n<center><h1>301 Moved Permanently</h1></center>\\r\\\n        \\n<hr><center>nginx/1.13.12</center>\\r\\n</body>\\r\\n</html>\\r\\n\"}\n    headers:\n      Connection: [keep-alive]\n      Content-Length: ['186']\n      Content-Type: [text/html]\n      Date: ['Mon, 29 Oct 2018 13:06:23 GMT']\n      Location: ['https://slashdot.org/']\n      Server: [nginx/1.13.12]\n    status: {code: 301, message: Moved Permanently}\n- request:\n    body: null\n    headers:\n      Accept: ['*/*']\n      Accept-Encoding: ['gzip,deflate']\n      Cookie: ['']\n      DNT: ['1']\n      User-Agent: ['Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101\n          Firefox/61.0']\n    method: GET\n    uri: https://slashdot.org/\n  response:\n    body:\n      string: !!binary |\n        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\n    headers:\n      Cache-Control: [no-cache]\n      Connection: [keep-alive]\n      Content-Encoding: [gzip]\n      Content-Type: [text/html; charset=utf-8]\n      Date: ['Mon, 29 Oct 2018 13:06:25 GMT']\n      Pragma: [no-cache]\n      SLASH_LOG_DATA: [shtml]\n      Server: [nginx/1.13.12]\n      Strict-Transport-Security: [max-age=31536000]\n      X-XRDS-Location: ['https://slashdot.org/slashdot.xrds']\n    status: {code: 200, message: OK}\n- request:\n    body: null\n    headers:\n      Accept: ['*/*']\n      Accept-Encoding: ['gzip,deflate']\n      Cookie: ['']\n      DNT: ['1']\n      User-Agent: ['Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101\n          Firefox/61.0']\n    method: GET\n    uri: \"http://www.za\\u017C\\xF3\\u0142\\u0107g\\u0119\\u015Bl\\u0105ja\\u017A\\u0144.pl/\"\n  response:\n    body:\n      string: !!binary |\n        H4sIAAAAAAAAA51WzW7bRhA+J0DeYcpDTpLYuC2a2pSKWk7rFHFstGqM+FIMyZW0/Nlldpdhl6/R\n        Hg3kGQIDPhnw2/Tgm6EX6CwpQorsqnUAgT/ab2a++fablYIvDo7Hk7cnL+BwcvQKTn7bf/VyDH/2\n        ff/0q7HvH0wO2oWvB18+g4lCobnhUmDm+y9e/zV68jiYmzxr7gxjdzfcZGx0hteLi79hdnOZfYQE\n        r24Dv10gRM4MwtyYos/elfz90BtLYZgw/YktmAdR+zb0DPvD+C79HkA0R6WZGXIt+8+ff/Ndf8dz\n        qfxl1UdBKGPrHh4FBWhjM9bG9zHjM7ELEWVkas9rIIQePX1XSrP3AwjOKHcCApWMZcUZVNexhUJq\n        o6QQHGqBycce8Gs4kRlG1kVQX5o33+lqkUByU9sUcsIFfpc58IstdBSfzU3H5oin8oIo/MKS7VF3\n        mlipfN7I/OHqduAoBKECv0W9xiREFdecJa4VYXvUapKqhdE1PRuWagM1UuOK+FN7FX2vTUpNZpyq\n        WdAFi+yUR7VgEGdI4bpmM9l2jY0cCaMkAW2qYtOh57Z21/eLbFDxlBcs5jiQaua7N/8ExUxh7lGa\n        nNo77RAeGFQz2mHv9zBDkXqjokWyPPBxNHiIMmcyxKj+lJAmRlVVDawsTRmyQSRzv0ITzb9/P3z5\n        Zn//29Nnhztv3na86k7Z88tOWVqBZfRdspVN8PyStKCP4wuFklOmpULYV5hFjVR0zVutGtl3obXK\n        Zq2rD7ftwoOafipCXfyH7T6NGNc32kioQKcX6BwCqcyLkhCyIqKxpK1XWMU1Co49xx5iMhBMaUDd\n        gHCMke6ZXjML7WSqrLHOLT0or22FYumPmoI524V682gYwM+NJnLlTNZ4lS1jepCahWKdTdcsuqX6\n        MqsUklCuD0rn/ITNMsVgxhrz5giaCV3SNUfRBs+kmyPV6tSxK7MypLOvYwWxKlNUtX3QNm1OifNk\n        zFKpwp1wUGSd/w5LZWRFutM2FHrOi4KL2V3b3Qtz/vvXWjXTOZJkuZuArtrxlCk6k8/Yr+trm8UO\n        HE2MErYCbi0lQ4W1NcoWxqZ8rbmfSHpFnBvAooLjBjhZB27W3gxx2kvylo3mWzkYLJixlxENJlli\n        ReFHaWS75saa7M5R3a26hppmvGYZF7Jiq4itpXMZC0YnERlkTerNRo4cqhXgftU/t/Nl+SYxK/9P\n        eYf6jOor999zENEUtT/Mgb/8o/Dk8T9lh+FvdAgAAA==\n    headers:\n      Accept-Ranges: [bytes]\n      Content-Encoding: [gzip]\n      Content-Length: ['943']\n      Content-Type: [text/html]\n      Date: ['Mon, 29 Oct 2018 13:06:34 GMT']\n      Server: [Apache]\n      Set-Cookie: ['startBAK=R3415748110; path=/; expires=Mon, 29-Oct-2018 14:14:08\n          GMT', 'start=R118851658; path=/; expires=Mon, 29-Oct-2018 14:08:37 GMT']\n      Vary: [Accept-Encoding]\n      X-IPLB-Instance: ['17350']\n    status: {code: 200, message: OK}\n- request:\n    body: null\n    headers:\n      Accept: ['*/*']\n      Accept-Encoding: ['gzip,deflate']\n      Cookie: ['']\n      DNT: ['1']\n      User-Agent: ['Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101\n          Firefox/61.0']\n    method: GET\n    uri: http://slashdot.org/\n  response:\n    body: {string: \"<html>\\r\\n<head><title>301 Moved Permanently</title></head>\\r\\n\\\n        <body bgcolor=\\\"white\\\">\\r\\n<center><h1>301 Moved Permanently</h1></center>\\r\\\n        \\n<hr><center>nginx/1.13.12</center>\\r\\n</body>\\r\\n</html>\\r\\n\"}\n    headers:\n      Connection: [keep-alive]\n      Content-Length: ['186']\n      Content-Type: [text/html]\n      Date: ['Mon, 29 Oct 2018 13:07:42 GMT']\n      Location: ['https://slashdot.org/']\n      Server: [nginx/1.13.12]\n    status: {code: 301, message: Moved Permanently}\n- request:\n    body: null\n    headers:\n      Accept: ['*/*']\n      Accept-Encoding: ['gzip,deflate']\n      Cookie: ['']\n      DNT: ['1']\n      User-Agent: ['Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101\n          Firefox/61.0']\n    method: GET\n    uri: https://slashdot.org/\n  response:\n    body:\n      string: !!binary |\n        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\n        4vv/H2SqXHtnXQIA\n    headers:\n      Cache-Control: [no-cache]\n      Connection: [keep-alive]\n      Content-Encoding: [gzip]\n      Content-Type: [text/html; charset=utf-8]\n      Date: ['Mon, 29 Oct 2018 13:07:46 GMT']\n      Pragma: [no-cache]\n      SLASH_LOG_DATA: [shtml]\n      Server: [nginx/1.13.12]\n      Strict-Transport-Security: [max-age=31536000]\n      X-XRDS-Location: ['https://slashdot.org/slashdot.xrds']\n    status: {code: 200, message: OK}\n- request:\n    body: null\n    headers:\n      Accept: ['*/*']\n      Accept-Encoding: ['gzip,deflate']\n      Cookie: ['']\n      DNT: ['1']\n      User-Agent: ['Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101\n          Firefox/61.0']\n    method: GET\n    uri: \"http://www.za\\u017C\\xF3\\u0142\\u0107g\\u0119\\u015Bl\\u0105ja\\u017A\\u0144.pl/\"\n  response:\n    body:\n      string: !!binary |\n        H4sIAAAAAAAAA51WzW7bRhA+J0DeYcpDTpLYuC2a2pSKWk7rFHFstGqM+FIMyZW0/Nlldpdhl6/R\n        Hg3kGQIDPhnw2/Tgm6EX6CwpQorsqnUAgT/ab2a++fablYIvDo7Hk7cnL+BwcvQKTn7bf/VyDH/2\n        ff/0q7HvH0wO2oWvB18+g4lCobnhUmDm+y9e/zV68jiYmzxr7gxjdzfcZGx0hteLi79hdnOZfYQE\n        r24Dv10gRM4MwtyYos/elfz90BtLYZgw/YktmAdR+zb0DPvD+C79HkA0R6WZGXIt+8+ff/Ndf8dz\n        qfxl1UdBKGPrHh4FBWhjM9bG9zHjM7ELEWVkas9rIIQePX1XSrP3AwjOKHcCApWMZcUZVNexhUJq\n        o6QQHGqBycce8Gs4kRlG1kVQX5o33+lqkUByU9sUcsIFfpc58IstdBSfzU3H5oin8oIo/MKS7VF3\n        mlipfN7I/OHqduAoBKECv0W9xiREFdecJa4VYXvUapKqhdE1PRuWagM1UuOK+FN7FX2vTUpNZpyq\n        WdAFi+yUR7VgEGdI4bpmM9l2jY0cCaMkAW2qYtOh57Z21/eLbFDxlBcs5jiQaua7N/8ExUxh7lGa\n        nNo77RAeGFQz2mHv9zBDkXqjokWyPPBxNHiIMmcyxKj+lJAmRlVVDawsTRmyQSRzv0ITzb9/P3z5\n        Zn//29Nnhztv3na86k7Z88tOWVqBZfRdspVN8PyStKCP4wuFklOmpULYV5hFjVR0zVutGtl3obXK\n        Zq2rD7ftwoOafipCXfyH7T6NGNc32kioQKcX6BwCqcyLkhCyIqKxpK1XWMU1Co49xx5iMhBMaUDd\n        gHCMke6ZXjML7WSqrLHOLT0or22FYumPmoI524V682gYwM+NJnLlTNZ4lS1jepCahWKdTdcsuqX6\n        MqsUklCuD0rn/ITNMsVgxhrz5giaCV3SNUfRBs+kmyPV6tSxK7MypLOvYwWxKlNUtX3QNm1OifNk\n        zFKpwp1wUGSd/w5LZWRFutM2FHrOi4KL2V3b3Qtz/vvXWjXTOZJkuZuArtrxlCk6k8/Yr+trm8UO\n        HE2MErYCbi0lQ4W1NcoWxqZ8rbmfSHpFnBvAooLjBjhZB27W3gxx2kvylo3mWzkYLJixlxENJlli\n        ReFHaWS75saa7M5R3a26hppmvGYZF7Jiq4itpXMZC0YnERlkTerNRo4cqhXgftU/t/Nl+SYxK/9P\n        eYf6jOor999zENEUtT/Mgb/8o/Dk8T9lh+FvdAgAAA==\n    headers:\n      Accept-Ranges: [bytes]\n      Content-Encoding: [gzip]\n      Content-Length: ['943']\n      Content-Type: [text/html]\n      Date: ['Mon, 29 Oct 2018 13:07:48 GMT']\n      Server: [Apache]\n      Set-Cookie: ['startBAK=R3415744843; path=/; expires=Mon, 29-Oct-2018 14:08:21\n          GMT', 'start=R118851658; path=/; expires=Mon, 29-Oct-2018 14:08:21 GMT']\n      Vary: [Accept-Encoding]\n      X-IPLB-Instance: ['17521']\n    status: {code: 200, message: OK}\nversion: 1\n"
  },
  {
    "path": "tests/vcr_cassettes/test_search_by_multiple_tags_search_all.yaml",
    "content": "interactions:\n- request:\n    body: null\n    headers:\n      Accept: ['*/*']\n      Accept-Encoding: ['gzip,deflate']\n      Cookie: ['']\n      DNT: ['1']\n      User-Agent: ['Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101\n          Firefox/61.0']\n    method: GET\n    uri: http://slashdot.org/\n  response:\n    body: {string: \"<html>\\r\\n<head><title>301 Moved Permanently</title></head>\\r\\n\\\n        <body bgcolor=\\\"white\\\">\\r\\n<center><h1>301 Moved Permanently</h1></center>\\r\\\n        \\n<hr><center>nginx/1.13.12</center>\\r\\n</body>\\r\\n</html>\\r\\n\"}\n    headers:\n      Connection: [keep-alive]\n      Content-Length: ['186']\n      Content-Type: [text/html]\n      Date: ['Mon, 29 Oct 2018 13:27:43 GMT']\n      Location: ['https://slashdot.org/']\n      Server: [nginx/1.13.12]\n    status: {code: 301, message: Moved Permanently}\n- request:\n    body: null\n    headers:\n      Accept: ['*/*']\n      Accept-Encoding: ['gzip,deflate']\n      Cookie: ['']\n      DNT: ['1']\n      User-Agent: ['Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101\n          Firefox/61.0']\n    method: GET\n    uri: https://slashdot.org/\n  response:\n    body:\n      string: !!binary |\n        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\n        AgA=\n    headers:\n      Cache-Control: [no-cache]\n      Connection: [keep-alive]\n      Content-Encoding: [gzip]\n      Content-Type: [text/html; charset=utf-8]\n      Date: ['Mon, 29 Oct 2018 13:27:45 GMT']\n      Pragma: [no-cache]\n      SLASH_LOG_DATA: [shtml]\n      Server: [nginx/1.13.12]\n      Strict-Transport-Security: [max-age=31536000]\n      X-XRDS-Location: ['https://slashdot.org/slashdot.xrds']\n    status: {code: 200, message: OK}\n- request:\n    body: null\n    headers:\n      Accept: ['*/*']\n      Accept-Encoding: ['gzip,deflate']\n      Cookie: ['']\n      DNT: ['1']\n      User-Agent: ['Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101\n          Firefox/61.0']\n    method: GET\n    uri: \"http://www.za\\u017C\\xF3\\u0142\\u0107g\\u0119\\u015Bl\\u0105ja\\u017A\\u0144.pl/\"\n  response:\n    body:\n      string: !!binary |\n        H4sIAAAAAAAAA51WzW7bRhA+J0DeYcpDTpLYuC2a2pSKWk7rFHFstGqM+FIMyZW0/Nlldpdhl6/R\n        Hg3kGQIDPhnw2/Tgm6EX6CwpQorsqnUAgT/ab2a++fablYIvDo7Hk7cnL+BwcvQKTn7bf/VyDH/2\n        ff/0q7HvH0wO2oWvB18+g4lCobnhUmDm+y9e/zV68jiYmzxr7gxjdzfcZGx0hteLi79hdnOZfYQE\n        r24Dv10gRM4MwtyYos/elfz90BtLYZgw/YktmAdR+zb0DPvD+C79HkA0R6WZGXIt+8+ff/Ndf8dz\n        qfxl1UdBKGPrHh4FBWhjM9bG9zHjM7ELEWVkas9rIIQePX1XSrP3AwjOKHcCApWMZcUZVNexhUJq\n        o6QQHGqBycce8Gs4kRlG1kVQX5o33+lqkUByU9sUcsIFfpc58IstdBSfzU3H5oin8oIo/MKS7VF3\n        mlipfN7I/OHqduAoBKECv0W9xiREFdecJa4VYXvUapKqhdE1PRuWagM1UuOK+FN7FX2vTUpNZpyq\n        WdAFi+yUR7VgEGdI4bpmM9l2jY0cCaMkAW2qYtOh57Z21/eLbFDxlBcs5jiQaua7N/8ExUxh7lGa\n        nNo77RAeGFQz2mHv9zBDkXqjokWyPPBxNHiIMmcyxKj+lJAmRlVVDawsTRmyQSRzv0ITzb9/P3z5\n        Zn//29Nnhztv3na86k7Z88tOWVqBZfRdspVN8PyStKCP4wuFklOmpULYV5hFjVR0zVutGtl3obXK\n        Zq2rD7ftwoOafipCXfyH7T6NGNc32kioQKcX6BwCqcyLkhCyIqKxpK1XWMU1Co49xx5iMhBMaUDd\n        gHCMke6ZXjML7WSqrLHOLT0or22FYumPmoI524V682gYwM+NJnLlTNZ4lS1jepCahWKdTdcsuqX6\n        MqsUklCuD0rn/ITNMsVgxhrz5giaCV3SNUfRBs+kmyPV6tSxK7MypLOvYwWxKlNUtX3QNm1OifNk\n        zFKpwp1wUGSd/w5LZWRFutM2FHrOi4KL2V3b3Qtz/vvXWjXTOZJkuZuArtrxlCk6k8/Yr+trm8UO\n        HE2MErYCbi0lQ4W1NcoWxqZ8rbmfSHpFnBvAooLjBjhZB27W3gxx2kvylo3mWzkYLJixlxENJlli\n        ReFHaWS75saa7M5R3a26hppmvGYZF7Jiq4itpXMZC0YnERlkTerNRo4cqhXgftU/t/Nl+SYxK/9P\n        eYf6jOor999zENEUtT/Mgb/8o/Dk8T9lh+FvdAgAAA==\n    headers:\n      Accept-Ranges: [bytes]\n      Content-Encoding: [gzip]\n      Content-Length: ['943']\n      Content-Type: [text/html]\n      Date: ['Mon, 29 Oct 2018 13:27:50 GMT']\n      Server: [Apache]\n      Set-Cookie: ['startBAK=R3415777513; path=/; expires=Mon, 29-Oct-2018 14:40:11\n          GMT', 'start=R118851658; path=/; expires=Mon, 29-Oct-2018 14:32:10 GMT']\n      Vary: [Accept-Encoding]\n      X-IPLB-Instance: ['5238']\n    status: {code: 200, message: OK}\n- request:\n    body: null\n    headers:\n      Accept: ['*/*']\n      Accept-Encoding: ['gzip,deflate']\n      Cookie: ['']\n      DNT: ['1']\n      User-Agent: ['Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101\n          Firefox/61.0']\n    method: GET\n    uri: http://example.com/\n  response:\n    body:\n      string: !!binary |\n        H4sIADuBBVIAA41UQa/TMAy+71eYcgFpXfeAB1PXViBA4gIc4MIxa9zVWpOUJO02offfcdu9ruXt\n        QCu1jh1//mzHSZ5Jk/tzjVB6VWWL5PGHQmYL4Cfx5CvMPp+EqiuET0YJ0kk0aBfDFoVeQF4K69Cn\n        QeOLcBNAlE2Mpfd1iL8batPgo9EetQ+7sAHkwyoNPJ581IXfjlC3kLRQmAYt4bE21k/8jyR9mUps\n        KcewXyyBNHkSVehyUWF6d4Vy/szJdAwugXPngsG2M/IMf3qxX4r8sLem0TLMTWVsDM+LNb+vtuMW\n        JeyedAzrq6oWUpLez3QFMw0Loag6xxB8r1HDD6FdsITgC1YtesoFfMMGWTMqlvDBcgZLcLw1dGip\n        uCL2wkP/ldROSPfpx/B2va5PT3neowLReHOD7v3M4VbuxST+zliJNrRCUuNiuEO1nVAScUX6sOR/\n        S448ygnBR7jXmzebzQSx60UoMTdWeDLMVRuNU9D3CiUJeKHEKbxk+a7L8uW0ZfMO/k8mD6M0L+Sk\n        mPOKzfp+w/ZPadZz61jvsWRXEsM3ifojmnXyIomGeVwkXWo8nkzycpDLuyejyarBVmc/S3Igez2w\n        hM6LXUWu5F54AzuExrFYGAtUVY3zXdVbBBwQHc8Pe+eN4gFzK/hlGs753DmBZ+Th4F3Q9dXrSL40\n        jYfaEiPnhktBuu8n8Fq4A6feB63RKnKODaskqkfWCd8XFos06G6NOIqOx+OKhBYrY/fREM9Fl2hB\n        9tVY5PCMp/oYqxWDiawHTKK+Ukl0qVs0XG9/AQiVqov2BAAA\n    headers:\n      Cache-Control: [max-age=604800]\n      Content-Encoding: [gzip]\n      Content-Length: ['606']\n      Content-Type: [text/html; charset=UTF-8]\n      Date: ['Mon, 29 Oct 2018 13:28:00 GMT']\n      Etag: ['\"1541025663+gzip\"']\n      Expires: ['Mon, 05 Nov 2018 13:28:00 GMT']\n      Last-Modified: ['Fri, 09 Aug 2013 23:54:35 GMT']\n      Server: [ECS (oxr/83C5)]\n      Vary: [Accept-Encoding]\n      X-Cache: [HIT]\n    status: {code: 200, message: OK}\nversion: 1\n"
  },
  {
    "path": "tests/vcr_cassettes/test_search_by_multiple_tags_search_any.yaml",
    "content": "interactions:\n- request:\n    body: null\n    headers:\n      Accept: ['*/*']\n      Accept-Encoding: ['gzip,deflate']\n      Cookie: ['']\n      DNT: ['1']\n      User-Agent: ['Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101\n          Firefox/61.0']\n    method: GET\n    uri: http://slashdot.org/\n  response:\n    body: {string: \"<html>\\r\\n<head><title>301 Moved Permanently</title></head>\\r\\n\\\n        <body bgcolor=\\\"white\\\">\\r\\n<center><h1>301 Moved Permanently</h1></center>\\r\\\n        \\n<hr><center>nginx/1.13.12</center>\\r\\n</body>\\r\\n</html>\\r\\n\"}\n    headers:\n      Connection: [keep-alive]\n      Content-Length: ['186']\n      Content-Type: [text/html]\n      Date: ['Mon, 29 Oct 2018 13:53:02 GMT']\n      Location: ['https://slashdot.org/']\n      Server: [nginx/1.13.12]\n    status: {code: 301, message: Moved Permanently}\n- request:\n    body: null\n    headers:\n      Accept: ['*/*']\n      Accept-Encoding: ['gzip,deflate']\n      Cookie: ['']\n      DNT: ['1']\n      User-Agent: ['Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101\n          Firefox/61.0']\n    method: GET\n    uri: https://slashdot.org/\n  response:\n    body:\n      string: !!binary |\n        H4sIAAAAAAAAA+ydeX8bx7Gu/6Y+xRjWCcmEALhopUT6arOjxLJ1RTo55ygOf0NgSELCFgwgirJ9\n        P/t93qrumQEBUpRE2nFsJZbImZ5eqqurq99a+v5n9XpyNO5160dZ2s5GyfhkmG21JqNR1h8n+9lh\n        p5/U69vXFq4t3P/s8bePdv/n+RMrb494qG+Tbto/3Kpl/RpP76si/auaX2R9Vfr0yV2v5X4vG6d8\n        Px7Ws39NOm+2av9d/+5B/dGgN0zHnf1uVktag/6YtrdqT59sZe3DbKV1NBr0sq01Kqcb1+7nrVFn\n        ON4+7vTbg+NGJ9/LJqPB3qTfGfSTrWTt3v1mKBGLJvmotVVTo/lms5k2DvJ2v9Ea9Jo01XyVN/OD\n        8VHWy5pv6Oxg1BzSXCfPGsNB9+Sg0+02ep1+41Ve2/7oilu94UUqyLtpftQejBuD0SF9m/THo5Mz\n        vrNZWhxnb8fNV+mb1Ae8uH2tc5AszSPMcvLDtSTZ77x721BnOv3OeElP9KfZTMaDZJzl401+W3zc\n        yYfd9CT57uniZrKYdo/Tk3xxJZRdfD7Z73byI+b0m7SXqcRO6PW8Ml8PDgcqM4f2ebuZt7u8b+Rv\n        DstvHw36uThvpzUYWvWH3cF+2k0OR4PJ8IxiyVd6mXz34utqW1PUPGwPR/XWYPC6k+WNYTdU9NPy\n        vWs/XatMa7fTf52Msu5WLR+fdDPGmY1rydEoO9iqzXBOKy9ZJ0/77U52OGiKuryo+Tqq2Qzp922Y\n        UVXOPm/8q1Xnq/owUrYuqqwk8Xk/fVPfT0en3tt8MqNp67VI028zuu5gtJl8vrp26/atm2FgalJN\n        +5Ix9vjsLP5YOpj0W2OtoqV8ZbDSX0lXRiudlXcrGdyTv1zcGdPYzqCfjr7df5W1xovfb43u5S9H\n        32/prx9/jJ8vLcNZS3rW+Je9avzrxx9ffr/cGE7yo6V0dDjpMcP58k8rVqa7tfbHfnacPE7H2dLy\n        vc7WoNEaZfzypMui7I+X+stw3zseH2bj8Cx/eLKbHooDefty9ft7nUaan/RbW2v8pNWe3nvXGKaS\n        Yd8M2hn8nmej8cPsYDDKlhjS8rXkp+WwUFbag5b1aGUxLKOVgl+Pj48buYZdzzVuExrDTv+QVblI\n        8YIgizBSUv665K+Ykn5fhFpJFm/dUpkqsyEhvb2k096q7Vvf9IWE3xT7lAscLqpMkq/pJNGalzwY\n        HCRhavN2L2t30uSzra1kceCTFUvbkj9Vbiv54ScGwJ+f7O8za2zknXF2ZrXTtXrZatVW95t0lFgt\n        W6d6a+W9F3rfGA0G43ZnhEhn6VUXcw06Li1DzWuqa5geZt1B2qbcD9cW9FsvHb1G5txaXdu4sXrr\n        5t2bN1c31tc31m6z6hecznuBzpvJUmS9ZbHXbgeOWr4GMSIzF/XvtQf9bCm5vqINKh90s5Wkl57s\n        Zwncbs2qE430YJyN9mDf9kkLxlvYmtvAvcoXoSf+zd6YDvBRHBQMZ/zgb5N6+WJ6HNUKnanOqO9U\n        /6o1TjVVrdBG4z2rdu2cqmY6xxbzbb97gmgdDkbjZO2/Epi1leYI42sLTsYff0yWnqXjo8YIUTro\n        LS1vrzZW15YTnl9vpK/St0s/JO10nG5qmhcGQ2ZYVNpjt2aTzjS3ldkvCBgZQq+pdrOk4NR4bXgq\n        8yqvFJkh5bWFnxAc0+t44dN0mkSq1cL9cWeMpI576WbyTXacJ4irpJ+N2vkKwmVycJCMj9IxbDeG\n        yfL7Tf9GX89oVY+cb+q7yIWKSmXbkTS2e0nrKB3l2XhrMj6o35HeltCTUFMfwbpVa2c+fnaEShUf\n        0sVGogXFvLPI8iQfTEatzAY1zlpHffarQ7FEF1nf9iLHnfFRgn6YpW9OEtY8isB4cJxCgeTrTn/y\n        NoE3km+HWT/Z8co6eT6Bibz/lRHAFUMk/slWbXC4aXT6yCGo5kCVqTp/dcQRm80ZysH+ZtpGwUVj\n        CQtiq7a2qj+37t66s75+Y219Lgn4zpZfp135cP3W7bt3b66vr965eUtfFe05Q73pZMda/5Uvjjvt\n        8dGWGlxJJuzR9byVdlPOAVsnGVxPxzq9Sc+eZluU6aVvKw/WEBG1pFlOkbeTDofdrN4b7CMX6sfZ\n        fp0H9VY6VL2VtmmhMrRzPmVjH09yU8JMh6vUsd9FOYhDLbXHVtof9DsMJSqPUQGe2sfUb5Go/C7t\n        srL7rIdaYjs47CuxyTrM4Hkx/xIUqBvVNpNbN1aHb5erTXC86TWm2kg0DdONVFVbO0eUWmps1Rtc\n        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\n        jaJSOcLO847AlU58//8DARhRP9pfAgA=\n    headers:\n      Cache-Control: [no-cache]\n      Connection: [keep-alive]\n      Content-Encoding: [gzip]\n      Content-Type: [text/html; charset=utf-8]\n      Date: ['Mon, 29 Oct 2018 13:53:04 GMT']\n      Pragma: [no-cache]\n      SLASH_LOG_DATA: [shtml]\n      Server: [nginx/1.13.12]\n      Strict-Transport-Security: [max-age=31536000]\n      X-XRDS-Location: ['https://slashdot.org/slashdot.xrds']\n    status: {code: 200, message: OK}\n- request:\n    body: null\n    headers:\n      Accept: ['*/*']\n      Accept-Encoding: ['gzip,deflate']\n      Cookie: ['']\n      DNT: ['1']\n      User-Agent: ['Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101\n          Firefox/61.0']\n    method: GET\n    uri: \"http://www.za\\u017C\\xF3\\u0142\\u0107g\\u0119\\u015Bl\\u0105ja\\u017A\\u0144.pl/\"\n  response:\n    body:\n      string: !!binary |\n        H4sIAAAAAAAAA51WzW7bRhA+J0DeYcpDTpLYuC2a2pSKWk7rFHFstGqM+FIMyZW0/Nlldpdhl6/R\n        Hg3kGQIDPhnw2/Tgm6EX6CwpQorsqnUAgT/ab2a++fablYIvDo7Hk7cnL+BwcvQKTn7bf/VyDH/2\n        ff/0q7HvH0wO2oWvB18+g4lCobnhUmDm+y9e/zV68jiYmzxr7gxjdzfcZGx0hteLi79hdnOZfYQE\n        r24Dv10gRM4MwtyYos/elfz90BtLYZgw/YktmAdR+zb0DPvD+C79HkA0R6WZGXIt+8+ff/Ndf8dz\n        qfxl1UdBKGPrHh4FBWhjM9bG9zHjM7ELEWVkas9rIIQePX1XSrP3AwjOKHcCApWMZcUZVNexhUJq\n        o6QQHGqBycce8Gs4kRlG1kVQX5o33+lqkUByU9sUcsIFfpc58IstdBSfzU3H5oin8oIo/MKS7VF3\n        mlipfN7I/OHqduAoBKECv0W9xiREFdecJa4VYXvUapKqhdE1PRuWagM1UuOK+FN7FX2vTUpNZpyq\n        WdAFi+yUR7VgEGdI4bpmM9l2jY0cCaMkAW2qYtOh57Z21/eLbFDxlBcs5jiQaua7N/8ExUxh7lGa\n        nNo77RAeGFQz2mHv9zBDkXqjokWyPPBxNHiIMmcyxKj+lJAmRlVVDawsTRmyQSRzv0ITzb9/P3z5\n        Zn//29Nnhztv3na86k7Z88tOWVqBZfRdspVN8PyStKCP4wuFklOmpULYV5hFjVR0zVutGtl3obXK\n        Zq2rD7ftwoOafipCXfyH7T6NGNc32kioQKcX6BwCqcyLkhCyIqKxpK1XWMU1Co49xx5iMhBMaUDd\n        gHCMke6ZXjML7WSqrLHOLT0or22FYumPmoI524V682gYwM+NJnLlTNZ4lS1jepCahWKdTdcsuqX6\n        MqsUklCuD0rn/ITNMsVgxhrz5giaCV3SNUfRBs+kmyPV6tSxK7MypLOvYwWxKlNUtX3QNm1OifNk\n        zFKpwp1wUGSd/w5LZWRFutM2FHrOi4KL2V3b3Qtz/vvXWjXTOZJkuZuArtrxlCk6k8/Yr+trm8UO\n        HE2MErYCbi0lQ4W1NcoWxqZ8rbmfSHpFnBvAooLjBjhZB27W3gxx2kvylo3mWzkYLJixlxENJlli\n        ReFHaWS75saa7M5R3a26hppmvGYZF7Jiq4itpXMZC0YnERlkTerNRo4cqhXgftU/t/Nl+SYxK/9P\n        eYf6jOor999zENEUtT/Mgb/8o/Dk8T9lh+FvdAgAAA==\n    headers:\n      Accept-Ranges: [bytes]\n      Content-Encoding: [gzip]\n      Content-Length: ['943']\n      Content-Type: [text/html]\n      Date: ['Mon, 29 Oct 2018 13:53:31 GMT']\n      Server: [Apache]\n      Set-Cookie: ['startBAK=R3415777513; path=/; expires=Mon, 29-Oct-2018 14:54:44\n          GMT', 'start=R118851658; path=/; expires=Mon, 29-Oct-2018 15:04:07 GMT']\n      Vary: [Accept-Encoding]\n      X-IPLB-Instance: ['17521']\n    status: {code: 200, message: OK}\n- request:\n    body: null\n    headers:\n      Accept: ['*/*']\n      Accept-Encoding: ['gzip,deflate']\n      Cookie: ['']\n      DNT: ['1']\n      User-Agent: ['Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101\n          Firefox/61.0']\n    method: GET\n    uri: http://example.com/\n  response:\n    body:\n      string: !!binary |\n        H4sIADuBBVIAA41UQa/TMAy+71eYcgFpXfeAB1PXViBA4gIc4MIxa9zVWpOUJO02offfcdu9ruXt\n        QCu1jh1//mzHSZ5Jk/tzjVB6VWWL5PGHQmYL4Cfx5CvMPp+EqiuET0YJ0kk0aBfDFoVeQF4K69Cn\n        QeOLcBNAlE2Mpfd1iL8batPgo9EetQ+7sAHkwyoNPJ581IXfjlC3kLRQmAYt4bE21k/8jyR9mUps\n        KcewXyyBNHkSVehyUWF6d4Vy/szJdAwugXPngsG2M/IMf3qxX4r8sLem0TLMTWVsDM+LNb+vtuMW\n        JeyedAzrq6oWUpLez3QFMw0Loag6xxB8r1HDD6FdsITgC1YtesoFfMMGWTMqlvDBcgZLcLw1dGip\n        uCL2wkP/ldROSPfpx/B2va5PT3neowLReHOD7v3M4VbuxST+zliJNrRCUuNiuEO1nVAScUX6sOR/\n        S448ygnBR7jXmzebzQSx60UoMTdWeDLMVRuNU9D3CiUJeKHEKbxk+a7L8uW0ZfMO/k8mD6M0L+Sk\n        mPOKzfp+w/ZPadZz61jvsWRXEsM3ifojmnXyIomGeVwkXWo8nkzycpDLuyejyarBVmc/S3Igez2w\n        hM6LXUWu5F54AzuExrFYGAtUVY3zXdVbBBwQHc8Pe+eN4gFzK/hlGs753DmBZ+Th4F3Q9dXrSL40\n        jYfaEiPnhktBuu8n8Fq4A6feB63RKnKODaskqkfWCd8XFos06G6NOIqOx+OKhBYrY/fREM9Fl2hB\n        9tVY5PCMp/oYqxWDiawHTKK+Ukl0qVs0XG9/AQiVqov2BAAA\n    headers:\n      Cache-Control: [max-age=604800]\n      Content-Encoding: [gzip]\n      Content-Length: ['606']\n      Content-Type: [text/html; charset=UTF-8]\n      Date: ['Mon, 29 Oct 2018 13:53:34 GMT']\n      Etag: ['\"1541025663+gzip\"']\n      Expires: ['Mon, 05 Nov 2018 13:53:34 GMT']\n      Last-Modified: ['Fri, 09 Aug 2013 23:54:35 GMT']\n      Server: [ECS (oxr/83C5)]\n      Vary: [Accept-Encoding]\n      X-Cache: [HIT]\n    status: {code: 200, message: OK}\nversion: 1\n"
  },
  {
    "path": "tests/vcr_cassettes/test_search_by_tags_enforces_space_seprations_exclusion.yaml",
    "content": "interactions:\n- request:\n    body: null\n    headers:\n      Accept: ['*/*']\n      Accept-Encoding: ['gzip,deflate']\n      Cookie: ['']\n      DNT: ['1']\n      User-Agent: ['Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101\n          Firefox/61.0']\n    method: GET\n    uri: https://bookmark2.com/\n  response:\n    body: {string: 'A client error occurred: '}\n    headers:\n      CF-RAY: [4715f7345f2caa4a-SIN]\n      Connection: [keep-alive]\n      Content-Length: ['25']\n      Content-Type: [text/plain; charset=utf-8]\n      Date: ['Mon, 29 Oct 2018 13:25:00 GMT']\n      Expect-CT: ['max-age=604800, report-uri=\"https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct\"']\n      Server: [cloudflare]\n      Set-Cookie: ['__cfduid=dfed596f417f6cf01aa1751eb7a78f0881540819500; expires=Tue,\n          29-Oct-19 13:25:00 GMT; path=/; domain=.bookmark2.com; HttpOnly']\n    status: {code: 404, message: Not Found}\nversion: 1\n"
  },
  {
    "path": "tox.bat",
    "content": "@if not defined BASEPYTHON set BASEPYTHON=python\r\n@tox.exe %*\r\n"
  },
  {
    "path": "tox.ini",
    "content": "[tox]\nenvlist = py310,py311,py312,py313,py314,pylint,flake8\n\n[flake8]\nmax-line-length = 139\nexclude =\n    .tox\n    build\n    venv\nignore =\n    # C901 func is too complex\n    C901,\n    # E126 continuation line over-indented for hanging indent\n    E126,\n    # E127 continuation line over-indented for visual indent\n    E127,\n    # E226 missing whitespace around arithmetic operator\n    E226,\n    # E231 missing whitespace after ','\n    E231,\n    # E302 expected 2 blank lines, found 1\n    E302,\n    # E305 expected 2 blank lines after class or function definition, found 1\n    E305,\n    # E731 do not assign a lambda expression, use a def\n    E731,\n    # W292 no newline at end of file\n    W292,\n    # W504 line break after binary operator\n    W504,\n    # E203 whitespace before :\n    E203,\n\n[pytest]\ntimeout = 10\ntimeout_method = thread\nmarkers =\n  non_tox: not run on tox\n  slow: slow tests\n  gui: GUI (functional) tests\n\n[testenv]\nusedevelop = true\ndependency_groups = tests\n; Tox installs groups first, which misinterprets our self-referential extras\n; Workaround by installing all extras manually: https://github.com/tox-dev/tox/issues/3561\nextras = server,locales\n\n[testenv:py310]\ncommands =\n    pytest --cov buku -vv -m \"not non_tox\" {posargs}\n\n[testenv:py311]\ncommands =\n    pytest --cov buku -vv -m \"not non_tox\" {posargs}\n\n[testenv:py312]\ncommands =\n    pytest --cov buku -vv -m \"not non_tox\" {posargs}\n\n[testenv:py313]\ncommands =\n    pytest --cov buku -vv -m \"not non_tox\" {posargs}\n\n[testenv:py314]\ncommands =\n    pytest --cov buku -vv -m \"not non_tox\" {posargs}\n\n[testenv:quick]\nbasepython = {env:BASEPYTHON:py313}\ncommands =\n    pytest --cov buku -vv -m \"not non_tox and not slow\" {posargs}\n\n[testenv:nogui]\nbasepython = {env:BASEPYTHON:py313}\ncommands =\n    pytest --cov buku -vv -m \"not non_tox and not gui\" {posargs}\n\n[testenv:pylint]\nbasepython = {env:BASEPYTHON:py313}\ncommands =\n    pylint . --rc-file tests/.pylintrc --recursive yes --ignore-paths .tox/,build/,venv/\n\n[testenv:flake8]\nbasepython = {env:BASEPYTHON:py313}\ncommands =\n    python -m flake8\n"
  }
]